mirror of
https://github.com/dzeiocom/markblog.git
synced 2025-06-19 14:09:18 +00:00
32
components/Button.tsx
Normal file
32
components/Button.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { Component } from "react";
|
||||
import { type } from "os";
|
||||
|
||||
import '../styl/styl.styl'
|
||||
|
||||
|
||||
export enum ButtonType {
|
||||
NORMAL = "",
|
||||
OUTLINE = "outline",
|
||||
GHOST = "ghost"
|
||||
}
|
||||
|
||||
interface Props {
|
||||
text: string
|
||||
type?: ButtonType
|
||||
// src: string
|
||||
// alt?: string
|
||||
// layout?: string
|
||||
}
|
||||
|
||||
export default class DWButton extends Component<Props, {}> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<button className={this.props.type}>{this.props.text}</button>
|
||||
)
|
||||
}
|
||||
}
|
89
components/Element.tsx
Normal file
89
components/Element.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { ChevronRight } from 'react-feather'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
date: Date
|
||||
image?: string
|
||||
link: string
|
||||
}
|
||||
|
||||
const months = [
|
||||
"le 13eme mois",
|
||||
'Janvier',
|
||||
'Février',
|
||||
'Mars',
|
||||
'Avril',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juillet',
|
||||
'Août',
|
||||
'Septembre',
|
||||
'Octobre',
|
||||
'Novembre',
|
||||
'Décembre'
|
||||
]
|
||||
|
||||
export default class Element extends React.Component<Props, {}> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Link href={this.props.link}>
|
||||
{this.props.image ? (
|
||||
<a><img src={this.props.image}/></a>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
<i>Le {this.props.date.getDate()} {months[this.props.date.getMonth()]} {this.props.date.getFullYear()}</i>
|
||||
<span>
|
||||
<Link href={this.props.link}>
|
||||
<a>{this.props.title}</a>
|
||||
</Link>
|
||||
<Link href={this.props.link}>
|
||||
<a><ChevronRight size={48}/></a>
|
||||
</Link>
|
||||
</span>
|
||||
<style jsx>{`
|
||||
div {
|
||||
padding: 5% 5%;
|
||||
max-width: 40%;
|
||||
min-width: 400px;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
}
|
||||
span {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
a, i {
|
||||
margin: 10px 0;
|
||||
display: block;
|
||||
}
|
||||
i {
|
||||
font-style: italic;
|
||||
font-size: 14px;
|
||||
}
|
||||
a {
|
||||
font-weight: bold;
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
158
components/Filters.tsx
Normal file
158
components/Filters.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
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'
|
||||
|
||||
interface Props {
|
||||
categories?: Category[]
|
||||
onQuery?: (query: string) => void
|
||||
onHeight?: (height: number) => void
|
||||
}
|
||||
|
||||
interface States {
|
||||
follow?: boolean
|
||||
height?: number
|
||||
}
|
||||
|
||||
export default class Filters extends React.Component<Props, States> {
|
||||
|
||||
private aside = undefined
|
||||
private input = undefined
|
||||
|
||||
constructor(props: Props) {
|
||||
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()
|
||||
}, 1);
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
if (this.input.value !== "") {
|
||||
this.submit()
|
||||
return
|
||||
}
|
||||
this.input.focus()
|
||||
}
|
||||
|
||||
submit = () => {
|
||||
if (this.props.onQuery) this.props.onQuery(this.input.value)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<aside ref={this.setRef} className={this.state && this.state.follow ? "follow" : ""}>
|
||||
<div>Trier</div>
|
||||
<div className="input icon-right">
|
||||
<select>
|
||||
<option value="plus récant au moins récent"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div>Filtrer</div>
|
||||
<div className="input icon-right">
|
||||
<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>
|
||||
))}
|
||||
|
||||
<style jsx>{`
|
||||
|
||||
aside {
|
||||
padding: 5% 3% 0;
|
||||
}
|
||||
|
||||
@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 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
div:not(.input) {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
background: linear-gradient(90deg, #45CAFC 0%, #4285F4 92.19%);
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
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;
|
||||
}
|
||||
|
||||
`}</style>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
}
|
32
components/Header.tsx
Normal file
32
components/Header.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
}
|
||||
|
||||
export default class Header extends React.Component<Props, {}> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<img src="/clouds.svg" alt=""/>
|
||||
|
||||
|
||||
<style jsx>{`
|
||||
div {
|
||||
position: relative;
|
||||
background: linear-gradient(90deg, #45CAFC 0%, #4285F4 92.19%);
|
||||
height: 207px;
|
||||
}
|
||||
img {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
min-width: 100%;
|
||||
height: 50px
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
46
components/Menu.tsx
Normal file
46
components/Menu.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface Props {
|
||||
}
|
||||
|
||||
export default class Menu extends React.Component<Props, {}> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<ul>
|
||||
<li>
|
||||
<Link href="/">
|
||||
<a>Home</a>
|
||||
</Link>
|
||||
</li>
|
||||
<style jsx>{`
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
background: white;
|
||||
width: calc(100% - 10px);
|
||||
padding: 5px;
|
||||
}
|
||||
a {
|
||||
color: black;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
transition: background 200ms
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: #45CAFC20
|
||||
}
|
||||
`}</style>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
}
|
125
components/Navbar.tsx
Normal file
125
components/Navbar.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React from 'react'
|
||||
import { Menu } from 'react-feather'
|
||||
import { timingSafeEqual } from 'crypto'
|
||||
|
||||
interface Props {
|
||||
height?: number
|
||||
}
|
||||
|
||||
interface States {
|
||||
scrolled: boolean
|
||||
}
|
||||
|
||||
export default class Navbar extends React.Component<Props, States> {
|
||||
|
||||
private height = 80
|
||||
|
||||
private menuRef = undefined
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
if (this.props.height) {
|
||||
this.height = this.props.height
|
||||
}
|
||||
}
|
||||
|
||||
setRef = element => {
|
||||
this.menuRef = element
|
||||
}
|
||||
|
||||
onScroll = () => {
|
||||
this.setState({
|
||||
scrolled: window.pageYOffset > 207
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('scroll', this.onScroll)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('scroll', this.onScroll)
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
this.menuRef.classList.toggle("shown")
|
||||
}
|
||||
|
||||
render() {
|
||||
const height = this.props.height || 80
|
||||
|
||||
// if (!this.state.scrolled)
|
||||
|
||||
// console.log(this.state.scrolled)
|
||||
return (
|
||||
<nav className={this.state && this.state.scrolled ? "scrolled" : ""}>
|
||||
<style jsx global>{`
|
||||
body {
|
||||
margin-top: ${height}px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<hr />
|
||||
<div className="head">
|
||||
<img src="/logo.svg" alt=""/>
|
||||
<span onClick={this.onClick} data-menu={this.refs.menu}>
|
||||
<Menu size={30} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div ref={this.setRef} className="menu">
|
||||
{this.props.children}
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
nav {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
transition: box-shadow 200ms
|
||||
}
|
||||
nav.scrolled {
|
||||
box-shadow: 0 0 10px 5px #00000040
|
||||
}
|
||||
img {
|
||||
height: 80%;
|
||||
}
|
||||
hr {
|
||||
height: 10px;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: linear-gradient(90deg, #45CAFC 0%, #4285F4 92.19%);
|
||||
}
|
||||
.menu {
|
||||
display: none
|
||||
}
|
||||
.menu.shown {
|
||||
display: block
|
||||
}
|
||||
.head {
|
||||
display: flex;
|
||||
height: ${height-10}px;
|
||||
background: white;
|
||||
padding-left: ${height-10}px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
flex-grow: 1;
|
||||
}
|
||||
span {
|
||||
width: ${height-10}px;
|
||||
height: ${height-10}px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
}
|
56
components/Post.ts
Normal file
56
components/Post.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import matter from 'gray-matter'
|
||||
|
||||
|
||||
interface PostInterface {
|
||||
slug: string
|
||||
title: string
|
||||
content: any
|
||||
}
|
||||
|
||||
interface PostHeader {
|
||||
title: string
|
||||
}
|
||||
|
||||
export default class Post implements PostInterface {
|
||||
|
||||
public slug: string
|
||||
public title: string
|
||||
public content: string
|
||||
public isStarted = false
|
||||
|
||||
constructor(slug: string) {
|
||||
this.slug = slug
|
||||
}
|
||||
|
||||
public async fetch() {
|
||||
console.log(this.slug)
|
||||
const content = await import(`../posts/${this.slug}.md`)
|
||||
const md = matter(content.default)
|
||||
this.title = md.data.title
|
||||
this.content = md.content
|
||||
}
|
||||
|
||||
public fetchSync() {
|
||||
console.log(this.slug)
|
||||
const content = require(`../posts/${this.slug}.md`)
|
||||
const md = matter(content.default)
|
||||
this.title = md.data.title
|
||||
this.content = md.content
|
||||
}
|
||||
|
||||
public static async fetchAll(): Promise<Post[]> {
|
||||
const files: string[] = ((require as any).context('../posts', true, /\.md$/)).keys()
|
||||
const posts: Post[] = []
|
||||
for (const file of files) {
|
||||
posts.push(
|
||||
new Post(
|
||||
file.replace(/^.*[\\\/]/, '')
|
||||
.split('.')
|
||||
.slice(0, -1)
|
||||
.join('.')
|
||||
)
|
||||
)
|
||||
}
|
||||
return posts
|
||||
}
|
||||
}
|
15
components/default.tsx
Normal file
15
components/default.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
}
|
||||
|
||||
export default class Name extends React.Component<Props, {}> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<span></span>
|
||||
)
|
||||
}
|
||||
}
|
4
components/interfaces/Category.ts
Normal file
4
components/interfaces/Category.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default interface Category {
|
||||
name: string
|
||||
slug: string
|
||||
}
|
Reference in New Issue
Block a user