Signed-off-by: Avior <florian.bouillon@delta-wings.net>
This commit is contained in:
Florian Bouillon 2019-12-16 23:59:00 +01:00
parent f19f4f43a6
commit 70f05363e2
No known key found for this signature in database
GPG Key ID: B143FF27EF555D16
33 changed files with 8242 additions and 0 deletions

8
.editorconfig Normal file
View File

@ -0,0 +1,8 @@
root = true
[*]
indent_style = tab
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
.env*
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

32
components/Button.tsx Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>
)
}
}

View File

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

49
export.js Executable file
View File

@ -0,0 +1,49 @@
const replace = require('replace-in-file')
const options = {
files: [
'out/**/*.html',
'out/**/*.js',
'out/*.html',
],
from: /_next/g,
to: 'assets',
countMatches: true
}
const options2 = {
files: [
'out/**/*.html',
'out/**/*.js',
'out/*.html',
],
from: /__next/g,
to: 'root',
countMatches: true
}
const options3 = {
files: [
'out/**/*.html',
'out/**/*.js',
'out/*.html',
],
from: /__NEXT_DATA__/g,
to: '__DZEIO_DATA__',
countMatches: true
}
async function run() {
try {
const res3 = await replace(options3)
console.log(res3)
const res2 = await replace(options2)
console.log(res2)
const results = await replace(options)
console.log(results)
} catch (error) {
console.error(error)
}
}
run()

2
next-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

40
next.config.js Normal file
View File

@ -0,0 +1,40 @@
const withCSS = require('@zeit/next-stylus')
const glob = require('glob')
const withOffline = require('next-offline')
// import posts from './posts/pages.json.ts'
// const posts = require('./posts/pages.json.ts')
// const t = require('./pages/portfolio/')
module.exports = withOffline(withCSS({
/* config options here */
exportTrailingSlash: true,
// cssModules: true,
// target: 'serverless',
webpack: function(config) {
config.module.rules.push({
test: /\.md$/,
use: 'raw-loader'
})
return config
},
plugins: [
["styled-jsx/babel", { "optimizeForSpeed": true }]
],
exportPathMap: async function() {
const paths = {
'/': { page: '/'},
'/portfolio': { page: '/portfolio'},
}
const posts = glob.sync('./posts/**/*.md')
posts.forEach(element => {
element = element.replace(/^.*[\\\/]/, '')
.split('.')
.slice(0, -1)
.join('.')
paths[`/portfolio/${element}`] = { page: '/portfolio/[slug]', query: { slug: element}}
});
return paths
}
}))

34
package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "website",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"export": "next export && node export.js && yarn move_next",
"start": "next start",
"move_next": "mv out/_next out/assets",
"serve": "serve out"
},
"dependencies": {
"@zeit/next-css": "^1.0.1",
"@zeit/next-stylus": "^1.0.1",
"glob": "^7.1.6",
"gray-matter": "^4.0.2",
"next": "9.1.5",
"next-offline": "^4.0.6",
"raw-loader": "^4.0.0",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-feather": "^2.0.3",
"react-markdown": "^4.2.2",
"replace-in-file": "^4.2.0",
"serve": "^11.2.0",
"stylus": "^0.54.7"
},
"devDependencies": {
"@types/node": "^12.12.17",
"@types/react": "^16.9.16",
"typescript": "^3.7.3"
}
}

23
pages/_app.tsx Normal file
View File

@ -0,0 +1,23 @@
import React from 'react'
import App from 'next/app'
class MyApp extends App {
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// static async getInitialProps(appContext) {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
//
// return { ...appProps }
// }
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}
export default MyApp

26
pages/_document.tsx Normal file
View File

@ -0,0 +1,26 @@
// _document is only rendered on the server side and not on the client side
// Event handlers like onClick can't be added to this file
// ./pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

124
pages/index.tsx Normal file
View File

@ -0,0 +1,124 @@
import { NextPage } from 'next'
import Head from 'next/head'
import Link from 'next/link'
import '../styl/styl.styl'
import Element from '../components/Element'
import Navbar from '../components/Navbar'
import Header from '../components/Header'
import Menu from '../components/Menu'
import Filters from '../components/Filters'
import Category from '../components/interfaces/Category'
import { Component } from 'react'
interface Props {
userAgent?: string
}
interface el {
link: string,
title: string,
img: string,
date: Date
}
interface States {
elements: el[]
asideHeight: number
}
// export const config = {amp: 'hybrid'}
const categories: Category[] = [
{
name: "test",
slug: "pouet"
}
]
const elements: el[] = [
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Dzeio.io, Services en ligne pour vous simplifier la vie !", img:"/sea.jpg", date: new Date()},
{link:"/studiomoto", title:"Loram ipsum dolor sit amet", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"j'aime les licornes et leurs jolie corne", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Pokémon ! attrapez les tous !", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"abcde", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"def", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"abc", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()},
{link:"/studiomoto", title:"Studiomoto, Site de référencement d'événement moto en France", img:"/uploads/stm.png", date: new Date()}
]
export default class Page extends Component<Props, States> {
onQuery = (query: string) => {
console.log(`query: ${query}`)
const t= elements.filter(el => {
return el.title.toLowerCase().includes(query.toLowerCase())
})
this.setState({
elements: t
})
}
onHeight = (height: number) => {
this.setState({
asideHeight: height
})
}
componentDidMount() {
this.setState({
elements
})
}
render() {
return (
<div>
<Head>
<link rel="manifest" href="/manifest.json" />
</Head>
<Navbar>
<Menu />
</Navbar>
<Header />
<main>
<span>
{this.state && this.state.elements.map((el, index) => (
<Element key={index} link={el.link} title={el.title} image={el.img} date={el.date} />
))}
</span>
<Filters categories={categories} onQuery={this.onQuery} onHeight={this.onHeight}/>
</main>
<style jsx>{`
span {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
main {
display: flex;
flex-direction: column;
}
main span {
flex-grow: 1;
}
@media (min-width: 820px) and (min-height: ${this.state && this.state.asideHeight ? this.state.asideHeight+100 : 600}px) {
main span {
max-width: calc(100% - 400px);
}
}
`}</style>
</div>
)
}
}

View File

@ -0,0 +1,28 @@
import { NextPage, NextPageContext } from "next"
import React from 'react'
import Post from "../../components/Post"
import ReactMarkdown from 'react-markdown'
interface Props {
post: Post
}
const PostPage: NextPage<Props> = (props: Props) => {
// React.
return (
<main>
<ReactMarkdown source={props.post.content}/>
</main>
)
}
PostPage.getInitialProps = async (context: NextPageContext) => {
const { slug } = context.query
if (typeof slug === "object") throw new Error("Slug is not correct")
const post = new Post(slug)
await post.fetch()
return {post}
}
export default PostPage

58
pages/portfolio/index.tsx Normal file
View File

@ -0,0 +1,58 @@
import { NextPage, NextPageContext } from "next"
import Link from 'next/link'
import Post from "../../components/Post"
// import posts from '../../posts/pages.json'
// import firstline from 'firstline'
// import 'fs'
interface Props {
files: fileInformations[]
}
interface fileInformations {
title: string
slug: string
}
const PortfolioIndex: NextPage<Props> = (props: Props) => {
const el: JSX.Element[] = []
for (const post of props.files) {
el.push(
)
}
return (
<ul>
{props.files.map(post => (
<li key={post.slug}>
<Link href="/portfolio/[slug]" as={`/portfolio/${post.slug}`}>
<a>{post.title}</a>
</Link>
</li>
))}
</ul>
)
}
PortfolioIndex.getInitialProps = async (context: NextPageContext) => {
const arr: fileInformations[] = []
for (const post of await Post.fetchAll()) {
if (!post.isStarted) await post.fetch()
arr.push({
slug: post.slug,
title: post.title
})
}
return {files: arr}
}
export default PortfolioIndex
function l(args: any) {
console.log(arguments)
}
async function test() {
}

5
posts/test.md Normal file
View File

@ -0,0 +1,5 @@
---
title: pouet :D
---
# Pouet :D

3
public/clouds.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="1440" height="55" viewBox="0 0 1440 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M-72.0044 55C-24.0044 1.66667 23.9956 1.66667 71.9956 55H-72.0044ZM-0.00439644 55C47.9956 -11.6667 95.9956 -11.6667 143.996 55H-0.00439644ZM71.9956 55C119.996 8.33333 167.996 8.33333 215.996 55H71.9956ZM143.996 55C191.996 -5 239.996 -5 287.996 55H143.996ZM215.996 55C263.996 8.33333 311.996 8.33333 359.996 55H215.996ZM287.996 55C335.996 -18.3333 383.996 -18.3333 431.996 55H287.996ZM359.996 55C407.996 -5 455.996 -5 503.996 55H359.996ZM431.996 55C479.996 8.33333 527.996 8.33333 575.996 55H431.996ZM503.996 55C551.996 -5 599.996 -5 647.996 55H503.996ZM575.996 55C623.996 21.6667 671.996 21.6667 719.996 55H575.996ZM647.996 55C695.996 1.66667 743.996 1.66667 791.996 55H647.996ZM719.996 55C767.996 15 815.996 15 863.996 55H719.996ZM791.996 55C839.996 28.3333 887.996 28.3333 935.996 55H791.996ZM863.996 55C911.996 21.6667 959.996 21.6667 1008 55H863.996ZM935.996 55C983.996 1.66667 1032 1.66667 1080 55H935.996ZM1008 55C1056 18.3333 1104 18.3333 1152 55H1008ZM1080 55C1128 8.33333 1176 8.33333 1224 55H1080ZM1152 55C1200 1.66667 1248 1.66667 1296 55H1152ZM1224 55C1272 21.6667 1320 21.6667 1368 55H1224ZM1296 55C1344 5 1392 5 1440 55H1296ZM1368 55C1416 -1.66667 1464 -1.66667 1512 55H1368Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

3
public/logo.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M72.8109 101.066L25 128.807L44.2174 84.4441L72.8109 101.066ZM79.7067 24.0024L125 128.807L45.0697 82.3519L70.2925 24.0024C71.8679 22.4526 73.4308 21.8064 74.9806 22.0652C76.556 21.7808 78.1321 22.427 79.7067 24.0024Z" fill="#4285F4"/>
</svg>

After

Width:  |  Height:  |  Size: 390 B

34
public/manifest.json Normal file
View File

@ -0,0 +1,34 @@
{
"short_name": "Gitea",
"name": "Gitea - Git with a cup of tea",
"icons": [
{
"src": "/img/gitea-lg.png",
"type": "image/png",
"sizes": "880x880"
},
{
"src": "/img/gitea-sm.png",
"type": "image/png",
"sizes": "120x120"
},
{
"src": "/img/gitea-512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "/img/gitea-192.png",
"type": "image/png",
"sizes": "192x192"
}
],
"serviceworker": {
"src": "/service-worker.js"
},
"start_url": "/",
"scope": "/",
"background_color": "#FAFAFA",
"display": "standalone",
"theme_color": "#6cc644"
}

BIN
public/sea.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
public/uploads/stm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

102
styl/include/_button.styl Normal file
View File

@ -0,0 +1,102 @@
$color = #4285F4
$hover = #70A8F8
$active = #3066D1
$outline-shadow = inset 0 0 0 2px $color
$outline-hover = rgba(179, 216, 253, 0.3)
$outline-active = rgba(66, 133, 244, 0.3)
$active-shadow = 0 0 0 3px rgba(204,204,204,.75)
button, a.button
cursor: pointer
color: #FFF
background-color: $color
border-radius: 5px
border: none
padding: 7px 14px
transition-property: box-shadow, background-color
transition-duration: 200ms
transition-timing-function: cubic-bezier(.2,0,.6,1)
margin: 3px
display: inline-flex
user-select: none
outline: none
/* Set Chrome on Firefox base */
line-height: 22px
text-decoration: none
box-sizing: border-box
&:hover
background-color: $hover
&:active
background-color: $active
&:focus:not(.disabled)
background-color: $active
box-shadow: $active-shadow
&::-moz-focus-inner
border: none
&.outline, &.ghost
color: $color
background-color: transparent
&:hover:not(:disabled):not(.disabled)
background-color: $outline-hover
&:active:not(:disabled):not(.disabled)
background-color: $outline-active
&.loading::after
border-color: transparent transparent $color $color
&.outline
box-shadow: $outline-shadow
&:focus
box-shadow: $outline-shadow, $active-shadow
&:disabled, &.disabled
background-color: #EEE
color: #BDBDBD
cursor: default
pointer-events: none
&.outline
box-shadow: 0 0 0 2px #BDBDBD
&.loading
color: transparent
position: relative
pointer-events: none
// cursor: default
&::after
content: ""
display: block
border: white 2px solid
border-color: transparent transparent white white
width: 1em
position: absolute
top: calc(50% - (1em / 2))
left: calc(50% - (1em / 2))
border-radius: 1000000000px
height: 1em
box-sizing: inherit
animation: ButtonLoading .5s infinite linear
a.button.outline:focus:not(.disabled),
a.button.ghost:focus:not(.disabled),
button.outline:focus, button.ghost:focus
background-color: $outline-active
@keyframes ButtonLoading
0%
transform: rotate(0)
100%
transform: rotate(365deg)

90
styl/include/_input.styl Normal file
View File

@ -0,0 +1,90 @@
$color = #4285F4
.input
position relative
*:not(input)
display inline
&.inline
display inline-block
&:not(:last-of-type)
margin-right 10px
input
display block
border-radius 10px
background-color #EEE
border none
padding 15px 20px
margin 3px
box-shadow inset 0 0 0 2px #BDBDBD
transition-property background-color, box-shadow, border
transition-duration 200ms
transition-timing-function cubic-bezier(.2, 0, .6, 1)
// Make input follow .input size
width: inherit
max-width calc(100% - 46px) // padding * 2 + margin * 2
&::placeholder
text-transform: lowercase
color #666
:disabled
background-color #EFEFEF
box-shadow none
&:not([readonly]):not([disabled])
&:hover
background-color #DDD
&:focus
background-color #FFF
box-shadow inset 0 0 0 2px $color, 0 0 0 3px rgba(204,204,204, .75)
label
text-transform uppercase
margin auto
font-weight 700
.helper
text-transform lowercase
font-weight 300
.status
float right
text-transform lowercase
font-weight 300
font-style italic
// Icons
i
width 25px
position absolute
left 28px
top 18px
// pointer-events none
color #666
transition color 200ms cubic-bezier(.2,0,.6,1)
&.icon-right i
left initial
right 28px
&.icon-left input
padding-left 50px
&.icon-right input
padding-right 50px
&.icon-right input, &.icon-left input
max-width calc(100% - 76px)
input:focus:not([readonly]) + i
color $color

6
styl/styl.styl Normal file
View File

@ -0,0 +1,6 @@
@import "include/_button"
@import "include/_input"
html, body {
margin 0
}

7
style.css Normal file
View File

@ -0,0 +1,7 @@
.card {
box-shadow: 0 0 5px black;
}
.backWhite {
background: white;
}

29
tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": [
"node_modules"
],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
]
}

6989
yarn.lock Normal file

File diff suppressed because it is too large Load Diff