Completely change sidebar structure + Further work towards sharing
This commit is contained in:
parent
54f5750ab5
commit
c6fcba4083
|
@ -1,3 +1,4 @@
|
||||||
|
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||||
import generateID from '../id-generator'
|
import generateID from '../id-generator'
|
||||||
import { replaceMatch } from './neo4j-helpers'
|
import { replaceMatch } from './neo4j-helpers'
|
||||||
|
|
||||||
|
@ -94,6 +95,27 @@ const Mutation = {
|
||||||
...createResult.records[0].get('share').properties,
|
...createResult.records[0].get('share').properties,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async deleteShareToken(root, args, ctx, info) {
|
||||||
|
if (!ctx.user.admin) {
|
||||||
|
const session = ctx.driver.session()
|
||||||
|
const result = await session.run(
|
||||||
|
`MATCH (u:User { id: {userId} })-[:SHARE_TOKEN]->(token:ShareToken { token: {token} })
|
||||||
|
RETURN token`,
|
||||||
|
{
|
||||||
|
userId: ctx.user.id,
|
||||||
|
token: args.token,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
if (result.records.length == 0) {
|
||||||
|
throw new Error('User is not allowed to delete this share')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return neo4jgraphql(root, args, ctx, info)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const Query = {
|
const Query = {
|
||||||
|
|
|
@ -143,8 +143,8 @@ export default async function processImage({ driver, markFinishedImage }, id) {
|
||||||
// Resize image
|
// Resize image
|
||||||
const thumbnailPath = path.resolve(imagePath, 'thumbnail.jpg')
|
const thumbnailPath = path.resolve(imagePath, 'thumbnail.jpg')
|
||||||
await sharp(originalPath)
|
await sharp(originalPath)
|
||||||
.jpeg({ quality: 80 })
|
.jpeg({ quality: 70 })
|
||||||
.resize(1440, 1080, { fit: 'inside', withoutEnlargement: true })
|
.resize(720, 480, { fit: 'inside', withoutEnlargement: true })
|
||||||
.toFile(thumbnailPath)
|
.toFile(thumbnailPath)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -117,6 +117,8 @@ type Mutation {
|
||||||
sharePhoto(photoId: ID!, expire: Date, password: String): ShareToken
|
sharePhoto(photoId: ID!, expire: Date, password: String): ShareToken
|
||||||
@isAuthenticated
|
@isAuthenticated
|
||||||
|
|
||||||
|
deleteShareToken(token: ID!): ShareToken @isAuthenticated
|
||||||
|
|
||||||
setAdmin(userId: ID!, admin: Boolean!): Result!
|
setAdmin(userId: ID!, admin: Boolean!): Result!
|
||||||
@hasRole(roles: [admin])
|
@hasRole(roles: [admin])
|
||||||
@neo4j_ignore
|
@neo4j_ignore
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { Component } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { NavLink } from 'react-router-dom'
|
import { NavLink } from 'react-router-dom'
|
||||||
import { Icon } from 'semantic-ui-react'
|
import { Icon } from 'semantic-ui-react'
|
||||||
|
import Sidebar from './components/sidebar/Sidebar'
|
||||||
import { Query } from 'react-apollo'
|
import { Query } from 'react-apollo'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ const Container = styled.div`
|
||||||
grid-template-columns: 80px 1fr 500px; */
|
grid-template-columns: 80px 1fr 500px; */
|
||||||
`
|
`
|
||||||
|
|
||||||
const LeftSidebar = styled.div`
|
const SideMenu = styled.div`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -87,7 +88,7 @@ class Layout extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<LeftSidebar>
|
<SideMenu>
|
||||||
<SideButton to="/photos" exact>
|
<SideButton to="/photos" exact>
|
||||||
<Icon name="image outline" />
|
<Icon name="image outline" />
|
||||||
<SideButtonLabel>Photos</SideButtonLabel>
|
<SideButtonLabel>Photos</SideButtonLabel>
|
||||||
|
@ -110,8 +111,10 @@ class Layout extends Component {
|
||||||
return null
|
return null
|
||||||
}}
|
}}
|
||||||
</Query>
|
</Query>
|
||||||
</LeftSidebar>
|
</SideMenu>
|
||||||
|
<Sidebar>
|
||||||
<Content>{this.props.children}</Content>
|
<Content>{this.props.children}</Content>
|
||||||
|
</Sidebar>
|
||||||
<Header>
|
<Header>
|
||||||
<Title>Photoview</Title>
|
<Title>Photoview</Title>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
|
@ -7,10 +7,13 @@ import PhotoGallery, {
|
||||||
presentIndexFromHash,
|
presentIndexFromHash,
|
||||||
} from '../../components/photoGallery/PhotoGallery'
|
} from '../../components/photoGallery/PhotoGallery'
|
||||||
import AlbumGallery from '../AllAlbumsPage/AlbumGallery'
|
import AlbumGallery from '../AllAlbumsPage/AlbumGallery'
|
||||||
|
import AlbumTitle from '../../components/AlbumTitle'
|
||||||
|
import { SidebarConsumer } from '../../components/sidebar/Sidebar'
|
||||||
|
|
||||||
const albumQuery = gql`
|
const albumQuery = gql`
|
||||||
query albumQuery($id: ID) {
|
query albumQuery($id: ID!) {
|
||||||
album(id: $id) {
|
album(id: $id) {
|
||||||
|
id
|
||||||
title
|
title
|
||||||
subAlbums(orderBy: title_asc) {
|
subAlbums(orderBy: title_asc) {
|
||||||
id
|
id
|
||||||
|
@ -111,6 +114,8 @@ class AlbumPage extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
<SidebarConsumer>
|
||||||
|
{({ updateSidebar }) => (
|
||||||
<Query query={albumQuery} variables={{ id: albumId }}>
|
<Query query={albumQuery} variables={{ id: albumId }}>
|
||||||
{({ loading, error, data }) => {
|
{({ loading, error, data }) => {
|
||||||
if (error) return <div>Error</div>
|
if (error) return <div>Error</div>
|
||||||
|
@ -133,7 +138,7 @@ class AlbumPage extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>{data.album && data.album.title}</h1>
|
<AlbumTitle album={data && data.album} disableLink />
|
||||||
{subAlbumElement}
|
{subAlbumElement}
|
||||||
{data.album && data.album.subAlbums.length > 0 && (
|
{data.album && data.album.subAlbums.length > 0 && (
|
||||||
<h2>Images</h2>
|
<h2>Images</h2>
|
||||||
|
@ -144,23 +149,21 @@ class AlbumPage extends Component {
|
||||||
activeIndex={this.state.activeImage}
|
activeIndex={this.state.activeImage}
|
||||||
presenting={this.state.presenting}
|
presenting={this.state.presenting}
|
||||||
onSelectImage={index => {
|
onSelectImage={index => {
|
||||||
|
updateSidebar(
|
||||||
|
<PhotoSidebar imageId={this.photos[index].id} />
|
||||||
|
)
|
||||||
this.setActiveImage(index)
|
this.setActiveImage(index)
|
||||||
}}
|
}}
|
||||||
setPresenting={this.setPresenting}
|
setPresenting={this.setPresenting}
|
||||||
nextImage={this.nextImage}
|
nextImage={this.nextImage}
|
||||||
previousImage={this.previousImage}
|
previousImage={this.previousImage}
|
||||||
/>
|
/>
|
||||||
<PhotoSidebar
|
|
||||||
imageId={
|
|
||||||
this.photos.length > 0 && this.state.activeImage != -1
|
|
||||||
? this.photos[this.state.activeImage].id
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Query>
|
</Query>
|
||||||
|
)}
|
||||||
|
</SidebarConsumer>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@ import React, { Component } from 'react'
|
||||||
import Layout from '../../Layout'
|
import Layout from '../../Layout'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { Query } from 'react-apollo'
|
import { Query } from 'react-apollo'
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
import PhotoGallery from '../../components/photoGallery/PhotoGallery'
|
import PhotoGallery from '../../components/photoGallery/PhotoGallery'
|
||||||
import PhotoSidebar from '../../components/sidebar/PhotoSidebar'
|
import PhotoSidebar from '../../components/sidebar/PhotoSidebar'
|
||||||
|
import AlbumTitle from '../../components/AlbumTitle'
|
||||||
|
import { SidebarConsumer } from '../../components/sidebar/Sidebar'
|
||||||
|
|
||||||
const photoQuery = gql`
|
const photoQuery = gql`
|
||||||
query allPhotosPage {
|
query allPhotosPage {
|
||||||
|
@ -85,6 +86,8 @@ class PhotosPage extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
<SidebarConsumer>
|
||||||
|
{({ updateSidebar }) => (
|
||||||
<Query query={photoQuery}>
|
<Query query={photoQuery}>
|
||||||
{({ loading, error, data }) => {
|
{({ loading, error, data }) => {
|
||||||
if (error) return error
|
if (error) return error
|
||||||
|
@ -96,13 +99,14 @@ class PhotosPage extends Component {
|
||||||
if (data.myAlbums) {
|
if (data.myAlbums) {
|
||||||
galleryGroups = data.myAlbums.map((album, index) => (
|
galleryGroups = data.myAlbums.map((album, index) => (
|
||||||
<div key={album.id}>
|
<div key={album.id}>
|
||||||
<Link to={`/album/${album.id}`}>
|
<AlbumTitle album={album} />
|
||||||
<h1 style={{ color: 'black', margin: '24px 0 12px 0' }}>
|
|
||||||
{album.title}
|
|
||||||
</h1>
|
|
||||||
</Link>
|
|
||||||
<PhotoGallery
|
<PhotoGallery
|
||||||
onSelectImage={photoIndex => {
|
onSelectImage={photoIndex => {
|
||||||
|
updateSidebar(
|
||||||
|
<PhotoSidebar
|
||||||
|
imageId={album.photos[photoIndex].id}
|
||||||
|
/>
|
||||||
|
)
|
||||||
this.setActiveImage(index, photoIndex)
|
this.setActiveImage(index, photoIndex)
|
||||||
}}
|
}}
|
||||||
activeIndex={
|
activeIndex={
|
||||||
|
@ -131,14 +135,11 @@ class PhotosPage extends Component {
|
||||||
].id
|
].id
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <div>{galleryGroups}</div>
|
||||||
<div>
|
|
||||||
{galleryGroups}
|
|
||||||
<PhotoSidebar imageId={activeImage} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
</Query>
|
</Query>
|
||||||
|
)}
|
||||||
|
</SidebarConsumer>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Icon } from 'semantic-ui-react'
|
||||||
|
import { SidebarConsumer } from './sidebar/Sidebar'
|
||||||
|
|
||||||
|
const Header = styled.h1`
|
||||||
|
color: black;
|
||||||
|
margin: 24px 0 12px 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
const SettingsIcon = props => {
|
||||||
|
const StyledIcon = styled(Icon)`
|
||||||
|
margin-left: 8px !important;
|
||||||
|
display: inline-block;
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1e70bf;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
return <StyledIcon name="edit outline" size="small" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const AlbumTitle = ({ album, disableLink = false }) => {
|
||||||
|
if (!album) return null
|
||||||
|
|
||||||
|
let title = <span>{album.title}</span>
|
||||||
|
|
||||||
|
if (!disableLink) {
|
||||||
|
title = <Link to={`/album/${album.id}`}>{title}</Link>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarConsumer>
|
||||||
|
{({ updateSidebar }) => (
|
||||||
|
<Header>
|
||||||
|
{title}
|
||||||
|
<SettingsIcon
|
||||||
|
onClick={() => {
|
||||||
|
updateSidebar(<div>Title stuff {album.title}</div>)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Header>
|
||||||
|
)}
|
||||||
|
</SidebarConsumer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AlbumTitle
|
|
@ -23,8 +23,9 @@ const PreventScroll = createGlobalStyle`
|
||||||
`
|
`
|
||||||
|
|
||||||
const imageQuery = gql`
|
const imageQuery = gql`
|
||||||
query presentImage($id: ID) {
|
query presentImage($id: ID!) {
|
||||||
photo(id: $id) {
|
photo(id: $id) {
|
||||||
|
id
|
||||||
title
|
title
|
||||||
original {
|
original {
|
||||||
width
|
width
|
||||||
|
@ -57,23 +58,30 @@ const PresentView = ({
|
||||||
<PreventScroll />
|
<PreventScroll />
|
||||||
<Query query={imageQuery} variables={{ id: image }}>
|
<Query query={imageQuery} variables={{ id: image }}>
|
||||||
{({ loading, error, data }) => {
|
{({ loading, error, data }) => {
|
||||||
if (error) return error
|
if (error) {
|
||||||
|
alert(error)
|
||||||
|
return <div>{error.message}</div>
|
||||||
|
}
|
||||||
|
|
||||||
let original = null
|
let original = null
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
const { photo } = data
|
const { photo } = data
|
||||||
original = (
|
original = (
|
||||||
<PresentImage
|
<PresentImage
|
||||||
|
// style={{ display: 'none' }}
|
||||||
src={photo && photo.original.url}
|
src={photo && photo.original.url}
|
||||||
onLoad={imageLoadedCallback && imageLoadedCallback()}
|
onLoad={e => {
|
||||||
|
// e.target.style.display = 'initial'
|
||||||
|
imageLoadedCallback && imageLoadedCallback()
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PresentImage src={thumbnail} />
|
|
||||||
{original}
|
{original}
|
||||||
|
<PresentImage src={thumbnail} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -35,17 +35,6 @@ const photoQuery = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const RightSidebar = styled.div`
|
|
||||||
height: 100%;
|
|
||||||
width: 500px;
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
top: 60px;
|
|
||||||
background-color: white;
|
|
||||||
padding: 12px;
|
|
||||||
border-left: 1px solid #eee;
|
|
||||||
`
|
|
||||||
|
|
||||||
const PreviewImage = styled(ProtectedImage)`
|
const PreviewImage = styled(ProtectedImage)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 333px;
|
height: 333px;
|
||||||
|
@ -71,16 +60,16 @@ const exifNameLookup = {
|
||||||
flash: 'Flash',
|
flash: 'Flash',
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlbumSidebar extends Component {
|
class PhotoSidebar extends Component {
|
||||||
render() {
|
render() {
|
||||||
const { imageId } = this.props
|
const { imageId } = this.props
|
||||||
|
|
||||||
if (!imageId) {
|
if (!imageId) {
|
||||||
return <RightSidebar />
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RightSidebar>
|
<div>
|
||||||
<Query query={photoQuery} variables={{ id: imageId }}>
|
<Query query={photoQuery} variables={{ id: imageId }}>
|
||||||
{({ loading, error, data }) => {
|
{({ loading, error, data }) => {
|
||||||
if (error) return error
|
if (error) return error
|
||||||
|
@ -129,9 +118,9 @@ class AlbumSidebar extends Component {
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Query>
|
</Query>
|
||||||
</RightSidebar>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AlbumSidebar
|
export default PhotoSidebar
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Query } from 'react-apollo'
|
import { Query, Mutation } from 'react-apollo'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { Table, Button, Icon, Dropdown } from 'semantic-ui-react'
|
import { Table, Button, Icon, Dropdown } from 'semantic-ui-react'
|
||||||
|
|
||||||
|
@ -11,6 +11,26 @@ const shareQuery = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const addShareMutation = gql`
|
||||||
|
mutation sidebarAddShare(
|
||||||
|
$photoId: ID!
|
||||||
|
$password: String
|
||||||
|
$expire: _Neo4jDateInput
|
||||||
|
) {
|
||||||
|
sharePhoto(photoId: $photoId, password: $password, expire: $expire) {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const deleteShareMutation = gql`
|
||||||
|
mutation sidebareDeleteShare($token: ID!) {
|
||||||
|
deleteShareToken(token: $token) {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const SidebarShare = ({ photo }) => {
|
const SidebarShare = ({ photo }) => {
|
||||||
if (!photo || !photo.id) return null
|
if (!photo || !photo.id) return null
|
||||||
|
|
||||||
|
@ -18,7 +38,7 @@ const SidebarShare = ({ photo }) => {
|
||||||
<div>
|
<div>
|
||||||
<h2>Sharing options</h2>
|
<h2>Sharing options</h2>
|
||||||
<Query query={shareQuery} variables={{ photoId: photo.id }}>
|
<Query query={shareQuery} variables={{ photoId: photo.id }}>
|
||||||
{({ loading, error, data }) => {
|
{({ loading, error, data, refetch }) => {
|
||||||
if (loading) return <div>Loading...</div>
|
if (loading) return <div>Loading...</div>
|
||||||
if (error) return <div>Error: {error}</div>
|
if (error) return <div>Error: {error}</div>
|
||||||
|
|
||||||
|
@ -32,7 +52,29 @@ const SidebarShare = ({ photo }) => {
|
||||||
<Button icon="chain" content="Copy" />
|
<Button icon="chain" content="Copy" />
|
||||||
<Dropdown button text="More">
|
<Dropdown button text="More">
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
<Dropdown.Item text="Delete" icon="delete" />
|
<Mutation
|
||||||
|
mutation={deleteShareMutation}
|
||||||
|
onCompleted={() => {
|
||||||
|
refetch()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(deleteShare, { loading, error, data }) => {
|
||||||
|
return (
|
||||||
|
<Dropdown.Item
|
||||||
|
text="Delete"
|
||||||
|
icon="delete"
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => {
|
||||||
|
deleteShare({
|
||||||
|
variables: {
|
||||||
|
token: share.token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Mutation>
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Button.Group>
|
</Button.Group>
|
||||||
|
@ -42,7 +84,7 @@ const SidebarShare = ({ photo }) => {
|
||||||
|
|
||||||
if (rows.length == 0) {
|
if (rows.length == 0) {
|
||||||
rows.push(
|
rows.push(
|
||||||
<Table.Row>
|
<Table.Row key="no-shares">
|
||||||
<Table.Cell colSpan="2">No shares found</Table.Cell>
|
<Table.Cell colSpan="2">No shares found</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
)
|
)
|
||||||
|
@ -62,7 +104,32 @@ const SidebarShare = ({ photo }) => {
|
||||||
<Table.Footer>
|
<Table.Footer>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.HeaderCell colSpan="2">
|
<Table.HeaderCell colSpan="2">
|
||||||
<Button content="New" floated="right" positive />
|
<Mutation
|
||||||
|
mutation={addShareMutation}
|
||||||
|
onCompleted={() => {
|
||||||
|
refetch()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(sharePhoto, { loading, error, data }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
content="Add share"
|
||||||
|
icon="add"
|
||||||
|
floated="right"
|
||||||
|
positive
|
||||||
|
loading={loading}
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => {
|
||||||
|
sharePhoto({
|
||||||
|
variables: {
|
||||||
|
photoId: photo.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Mutation>
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Footer>
|
</Table.Footer>
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { createContext } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Route } from 'react-router-dom'
|
||||||
|
|
||||||
|
const SidebarContainer = styled.div`
|
||||||
|
height: 100%;
|
||||||
|
width: 500px;
|
||||||
|
position: fixed;
|
||||||
|
overflow-y: scroll;
|
||||||
|
right: 0;
|
||||||
|
top: 60px;
|
||||||
|
background-color: white;
|
||||||
|
padding: 12px 12px 100px;
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const SidebarContext = createContext()
|
||||||
|
|
||||||
|
export const SidebarConsumer = SidebarContext.Consumer
|
||||||
|
|
||||||
|
const { Consumer, Provider } = SidebarContext
|
||||||
|
|
||||||
|
class Sidebar extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
content: 'Start value',
|
||||||
|
}
|
||||||
|
|
||||||
|
this.update = content => this.setState({ content })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Provider
|
||||||
|
value={{ updateSidebar: this.update, content: this.state.content }}
|
||||||
|
>
|
||||||
|
{this.props.children}
|
||||||
|
<Consumer>
|
||||||
|
{value => <SidebarContainer>{value.content}</SidebarContainer>}
|
||||||
|
</Consumer>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sidebar
|
Loading…
Reference in New Issue