Signed-off-by: Avior <florian.bouillon@delta-wings.net>
This commit is contained in:
2020-01-04 17:35:30 +01:00
parent e5fb01eea8
commit a0f1799114
51 changed files with 879 additions and 261 deletions

View File

@ -1,11 +1,13 @@
import React from 'react'
import Link from 'next/link'
import { ChevronRight } from 'react-feather'
import next from 'next'
interface Props {
title: string
date: Date
image?: string
alt?: string
link: string
}
@ -30,30 +32,40 @@ export default class Element extends React.Component<Props, {}> {
super(props)
}
render() {
let date = this.props.date
if (typeof this.props.date === "string") {
date = new Date(this.props.date)
}
const t = `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`
return (
<div>
<Link href={this.props.link}>
{this.props.image ? (
<a><img src={this.props.image}/></a>
<a><img src={this.props.image} alt={this.props.alt}/></a>
) : (
<div></div>
)}
</Link>
<i>Le {this.props.date.getDate()} {months[this.props.date.getMonth()]} {this.props.date.getFullYear()}</i>
<i>Le {t}</i>
<span>
<Link href={this.props.link}>
<a>{this.props.title}</a>
</Link>
<Link href={this.props.link}>
<Link as={this.props.link} href="/[category]/[slug]">
<a><ChevronRight size={48}/></a>
</Link>
</span>
<style jsx>{`
div {
padding: 5% 5%;
max-width: 40%;
min-width: 400px;
min-width: 90%;
}
@media (min-width: 840px) {
div {
max-width: 40%;
min-width: 400px;
}
}
img {
width: 100%;

View File

@ -1,13 +1,11 @@
import React from 'react'
import Link from 'next/link'
import Category from './interfaces/Category'
import '../styl/styl.styl'
import { ChevronRight } from 'react-feather'
import Router from 'next/router'
import { ChevronRight, ChevronDown } from 'react-feather'
interface Props {
categories?: Category[]
onQuery?: (query: string) => void
categories?: string[]
onQuery?: (query: string, sort?: boolean) => void
onHeight?: (height: number) => void
}
@ -25,20 +23,10 @@ export default class Filters extends React.Component<Props, States> {
super(props)
}
setRef = element => {
this.aside = element
}
setInput = element => {
this.input = element
}
onScroll = () => {
this.setState({
follow: window.pageYOffset > 217
})
}
onKeyDown = (ev: React.KeyboardEvent<HTMLInputElement>) => {
setTimeout(() => {
this.submit()
@ -53,73 +41,65 @@ export default class Filters extends React.Component<Props, States> {
this.input.focus()
}
submit = () => {
if (this.props.onQuery) this.props.onQuery(this.input.value)
onChange = (ev) => {
this.submit(ev.target.value === "true")
}
componentDidMount() {
this.onScroll()
this.setState({
height: this.aside.clientHeight
})
if (this.props.onHeight) this.props.onHeight(this.aside.clientHeight)
window.addEventListener('scroll', this.onScroll)
}
componentWillUnmount() {
window.removeEventListener('scroll', this.onScroll)
submit = (sort?: boolean) => {
if (this.props.onQuery) this.props.onQuery(this.input.value, sort)
}
render() {
return (
<aside ref={this.setRef} className={this.state && this.state.follow ? "follow" : ""}>
<aside>
<div>Trier</div>
<div className="input icon-right">
<select>
<option value="plus récant au moins récent"></option>
<select onChangeCapture={this.onChange}>
<option value="true">plus récent au moins récent</option>
<option value="false">moins récent au plus récent</option>
</select>
<i>
<ChevronDown />
</i>
</div>
<div>Filtrer</div>
<div className="input icon-right">
<div className="input icon-right inline">
<input placeholder="ex: dzeio.com" type="text" ref={this.setInput} onKeyDownCapture={this.onKeyDown} />
<i>
<ChevronRight onClick={this.onClick} />
</i>
</div>
<p>Languages :</p>
{this.props.categories.map(cat => (
<Link key={cat.slug} href={cat.slug}>
<a>{cat.name}</a>
</Link>
))}
<span>
{this.props.categories.map(cat => (
<Link key={cat} href="/tag/[tag]" as={`/tag/${cat.toLowerCase()}`}>
<a className="button">{cat}</a>
</Link>
))}
</span>
<style jsx>{`
aside {
padding: 5% 3% 0;
display: flex;
flex-direction: column;
max-width: 445px;
}
@media (min-width: 820px) and (min-height: ${this.state && this.state.height ? this.state.height+100 : 600}px) {
aside {
position: absolute;
min-width: calc(400px - 6%);
right: 0;
}
aside.follow {
position: fixed;
top: 70px;
}
}
.input {
/*.input {
display: flex;
justify-content: center;
margin: 20px 0;
}*/
span {
display: inline-flex;
flex-wrap: wrap;
}
a {
flex-grow: 1;
}
div:not(.input) {
@ -134,21 +114,8 @@ export default class Filters extends React.Component<Props, States> {
text-align: center;
border-radius: 10px;
}
a {
padding: 7px 12px;
background: #4285F4;
color: white;
text-transform: uppercase;
border-radius: 10px;
font-size: 20px;
text-decoration: none;
display: inline-block;
transition: background 200ms
}
a:hover {
background: #45CAFC;
.input {
align-self: center;
}
`}</style>

72
components/Footer.tsx Normal file
View File

@ -0,0 +1,72 @@
import React from 'react'
import { PhoneCall, Mail, GitHub, Twitter, Linkedin } from 'react-feather'
import Link from 'next/link'
interface Props {}
interface States {}
export default class Footer extends React.Component<Props, States> {
constructor(props: Props) {
super(props)
}
render() {
return (
<footer>
<div className="pre"></div>
<div className="footer">
<span>
<a href="mailto:contact@avior.me" target="_blank">
<Mail color="#4285F4" />
</a>
<a href="tel:+33672292683" target="_blank">
<PhoneCall color="#4285F4" />
</a>
<a href="https://git.delta-wings.net" target="_blank">
<GitHub color="#4285F4" />
</a>
<a href="https://twitter.com/aviortheking" target="_blank">
<Twitter color="#4285F4" />
</a>
<a href="https://www.linkedin.com/in/florian-bouillon/" target="_blank">
<Linkedin color="#4285F4" />
</a>
</span>
</div>
<style jsx>{`
footer {
padding-top: 50px;
}
.pre {
height: 20px;
background: #EEE;
}
.footer {
display: flex;
padding: 13px 10%;
justify-content: space-evenly;
}
.footer span {
display: flex;
justify-content: space-evenly;
width: 100%;
}
a {
padding: 10px
}
@media (min-width: 850px) {
.footer {
padding: 20px 0
}
.footer span {
width: 300px;
}
}
`}</style>
</footer>
)
}
}

View File

@ -10,20 +10,27 @@ export default class Header extends React.Component<Props, {}> {
render() {
return (
<div>
<img src="/clouds.svg" alt=""/>
{/* <p>Bienvenue sur le Portfolio de Florian BOUILLON !</p> */}
<style jsx>{`
div {
position: relative;
background: linear-gradient(90deg, #45CAFC 0%, #4285F4 92.19%);
height: 207px;
background: url('/clouds.svg'), linear-gradient(90deg, #45CAFC 0%, #4285F4 92.19%);
background-repeat: repeat-x;
background-position: bottom;
min-height: 207px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
color: white;
font-size: 35px;
text-transform: uppercase;
}
img {
position: absolute;
bottom: 0;
min-width: 100%;
height: 50px
height: 50px;
}
`}</style>
</div>

55
components/Layout.tsx Normal file
View File

@ -0,0 +1,55 @@
import React from 'react'
import Navbar from './Navbar'
import Menu from './Menu'
import Header from './Header'
import Footer from './Footer'
interface Props {
hasHeader?: boolean
headerChild?: JSX.Element
}
export default class Layout extends React.Component<Props, {}> {
constructor(props: Props) {
super(props)
}
render() {
return (
<div>
<Navbar>
<Menu />
</Navbar>
{this.props.hasHeader && this.props.headerChild ? (
<Header>{this.props.headerChild}</Header>
) : (
<Header />
)}
{this.props.children}
<Footer />
<style jsx>{`
div {
height: inherit;
width: inherit;
}
`}</style>
<style jsx global>{`
html {
height: 100%;
}
::selection {
background: #4285F4;
color: #FFF;
}
body {
height: calc(100% - 80px)
}
#root, #__next {
height: 100%;
width : 100%;
}
`}</style>
</div>
)
}
}

View File

@ -1,6 +1,7 @@
import React from 'react'
import { Menu } from 'react-feather'
import { timingSafeEqual } from 'crypto'
import Link from 'next/link'
interface Props {
height?: number
@ -59,11 +60,19 @@ export default class Navbar extends React.Component<Props, States> {
margin-top: ${height}px;
overflow-x: hidden;
}
.menu * {
pointer-events: none;
}
.menu.shown * {
pointer-events: initial;
}
`}</style>
<hr />
<div className="head">
<img src="/logo.svg" alt=""/>
<Link href="/">
<a><img src="/logo.svg" alt=""/></a>
</Link>
<span onClick={this.onClick} data-menu={this.refs.menu}>
<Menu size={30} />
</span>
@ -84,8 +93,11 @@ export default class Navbar extends React.Component<Props, States> {
nav.scrolled {
box-shadow: 0 0 10px 5px #00000040
}
img {
height: 80%;
a {
height: 100%;
flex-grow: 1;
display: flex;
justify-content: center;
}
hr {
height: 10px;
@ -94,10 +106,14 @@ export default class Navbar extends React.Component<Props, States> {
background: linear-gradient(90deg, #45CAFC 0%, #4285F4 92.19%);
}
.menu {
display: none
opacity: 0;
pointer-event: none;
height: 0;
transition: opacity 200ms cubic-bezier(.2,0,.6,1);
}
.menu.shown {
display: block
opacity: 1;
height: initial;
}
.head {
display: flex;
@ -109,10 +125,13 @@ export default class Navbar extends React.Component<Props, States> {
}
img {
flex-grow: 1;
height: 100%;
}
span {
width: ${height-10}px;
height: ${height-10}px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;

View File

@ -7,8 +7,15 @@ interface PostInterface {
content: any
}
interface PostHeader {
export interface PostHeader {
title: string
id: string
category: string
image?: string
imageAlt?: string
date: Date
url?: string
tags?: string[]
}
export default class Post implements PostInterface {
@ -18,23 +25,27 @@ export default class Post implements PostInterface {
public content: string
public isStarted = false
public header: PostHeader
constructor(slug: string) {
this.slug = slug
}
public async fetch() {
console.log(this.slug)
const content = await import(`../posts/${this.slug}.md`)
if (!this.slug.endsWith(".md")) this.slug = "portfolio/" + this.slug + ".md"
const content = await import(`../posts/${this.slug}`)
const md = matter(content.default)
this.title = md.data.title
this.header = (md.data as PostHeader)
this.content = md.content
}
public fetchSync() {
console.log(this.slug)
const content = require(`../posts/${this.slug}.md`)
if (!this.slug.endsWith(".md")) this.slug = "portfolio/" + this.slug + ".md"
const content = require(`../posts/${this.slug}`)
const md = matter(content.default)
this.title = md.data.title
this.header = (md.data as PostHeader)
this.content = md.content
}
@ -44,10 +55,7 @@ export default class Post implements PostInterface {
for (const file of files) {
posts.push(
new Post(
file.replace(/^.*[\\\/]/, '')
.split('.')
.slice(0, -1)
.join('.')
file.replace("./", '')
)
)
}

View File

@ -1,9 +1,10 @@
import React from 'react'
interface Props {
}
interface Props {}
export default class Name extends React.Component<Props, {}> {
interface States {}
export default class Name extends React.Component<Props, States> {
constructor(props: Props) {
super(props)
}

View File

@ -1,4 +0,0 @@
export default interface Category {
name: string
slug: string
}