commit 56d40196d63af52b434f2c2ed2cc32edfb20cdcb Author: Corentin Risselin Date: Tue Dec 31 06:03:44 2019 +0900 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65a4864 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +public + +*.png +*.jpg +*.svg +*.log +*.lock \ No newline at end of file diff --git a/assets/theme/dark.css b/assets/theme/dark.css new file mode 100644 index 0000000..1d185db --- /dev/null +++ b/assets/theme/dark.css @@ -0,0 +1,8 @@ +:root +{ + --main-bg-color: #333; + --main-fg-color: #eee; + + --main-primary-color: hsl(213, 35%, 65%); + --light-primary-color: hsl(213, 35%, 45%); +} \ No newline at end of file diff --git a/assets/theme/light.css b/assets/theme/light.css new file mode 100644 index 0000000..1ba7885 --- /dev/null +++ b/assets/theme/light.css @@ -0,0 +1,8 @@ +:root +{ + --main-bg-color: #eee; + --main-fg-color: #333; + + --main-primary-color: hsl(213, 35%, 45%); + --light-primary-color: hsl(213, 35%, 65%); +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..ea9f129 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + Inferno - Hello World + + + + +
+ + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..ded21dc --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "ayo", + "version": "0.1.0", + "main": "index.js", + "author": "Risselin Corentin", + "license": "MIT", + "dependencies": { + "@babel/cli": "^7.7.0", + "@babel/core": "^7.7.2", + "@babel/preset-env": "^7.7.1", + "babel-loader": "^8.0.6", + "babel-plugin-inferno": "^6.1.0", + "compression-webpack-plugin": "^3.0.0", + "inferno": "^7.3.2", + "inferno-redux": "^7.3.3", + "inferno-router": "^7.3.2", + "redux": "^4.0.5", + "webpack": "^4.41.2", + "webpack-cli": "^3.3.10" + }, + "devDependencies": { + "css-loader": "^3.4.0", + "file-loader": "^5.0.2", + "style-loader": "^1.1.2", + "webpack-dev-server": "^3.9.0" + }, + "scripts": { + "build": "webpack --config webpack.prod.js", + "dev": "webpack --config webpack.dev.js", + "start": "node server.js", + "start-dev": "webpack-dev-server --config webpack.dev.js" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..c71f1fc --- /dev/null +++ b/server.js @@ -0,0 +1,61 @@ +const fs = require('fs'); +const http = require('http'); +const path = require('path'); +const url = require('url'); + +const port = 8080; // Prefer port > 1024 to avoid super-user permission + +const server = http.createServer((request, response) => { + const mimeType = { + '.html': 'text/html', + '.js': 'text/javascript', + '.gz': 'application/javascript', + '.svg': 'image/svg+xml', + '.css': 'text/css' + }; + const mimeEncoding = { + '.gz': 'gzip', + }; + let pathname = '.' + url.parse(request.url).pathname; + fs.exists(pathname, function (exist) { + if(!exist) + { + // If the file is not found, return 404 + response.statusCode = 404; + response.end(`File ${pathname} not found!`); + return; + } + // If is a directory, then look for index.html + if (fs.statSync(pathname).isDirectory()) { + pathname += '/index.html'; + } + // Read file from file system + fs.readFile(pathname, function(error, data){ + if(error) + { + response.statusCode = 500; + response.end(`Error getting the file: ${error}.`); + } + else + { + // Based on the URL path, extract the file extention. e.g. .js, .doc, ... + const extension = path.parse(pathname).ext; + // Set Content-Type + response.setHeader('Content-Type', mimeType[extension] || 'text/plain' ); + // If the file is found, set Content-Encoding + if(mimeEncoding[extension]) + { + response.setHeader('Content-Encoding', mimeEncoding[extension]); + } + response.end(data); + } + }); + }); +}); + +server.listen(port, (error) => { + if (error) { + return console.log(`Server cannot listen port ${port} :`, error); + } + console.log(`Server is listening on port ${port}`); +}); diff --git a/src/actions.js b/src/actions.js new file mode 100644 index 0000000..be10263 --- /dev/null +++ b/src/actions.js @@ -0,0 +1,8 @@ +export const themeActions = { + THEME_CHANGE: 'THEME_CHANGE' +} + +export const changeTheme = (new_theme) => ({ + type: themeActions.THEME_CHANGE, + theme: new_theme +}) \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx new file mode 100644 index 0000000..7f692ee --- /dev/null +++ b/src/index.jsx @@ -0,0 +1,35 @@ +`use strict` +import { render } from 'inferno' +import { Provider } from 'inferno-redux'; +import { applyMiddleware, createStore, combineReducers } from 'redux'; + +import { themeReducer } from './reducers'; + +import Main from './main.jsx' + +const logger = process.env.DEBUG ? store => next => action => { + console.group(action.type) + console.info('dispatching', action) + let result = next(action) + console.log('next state', store.getState()) + console.groupEnd() + return result + } : null + +const persistedState = localStorage.getItem('reduxState') +const initState = persistedState ? JSON.parse(persistedState) : {} + +const reducers = combineReducers( + {theme: themeReducer}) +const store = process.env.DEBUG ? + createStore(reducers, initState, applyMiddleware(logger)) : createStore(reducers, initState) + +store.subscribe(() => { + localStorage.setItem('reduxState', JSON.stringify(store.getState())) +}) + +render( + +
+ , + document.getElementById("root")) diff --git a/src/main.css b/src/main.css new file mode 100644 index 0000000..d2194ff --- /dev/null +++ b/src/main.css @@ -0,0 +1,77 @@ +*, ::after, ::before +{ + box-sizing: border-box; +} + +body +{ + background-color: var(--main-bg-color); + color: var(--main-fg-color); + margin: 0; +} + +nav +{ + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; +} + +/* START switch */ +input:checked + .slider::before { + transform: translateX(20px); +} + +.switch input { + display: none; +} + +.slider.round +{ + border-radius: 34px; +} + +.slider +{ + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--light-primary-color); + border: 1px solid var(--light-primary-color); +} + +.slider.round::before +{ + border-radius: 50%; +} + +.slider::before +{ + background-color: var(--main-primary-color); + position: absolute; + content: ""; + height: 15px; + width: 15px; + left: 2px; + bottom: 1px; + transition: background-color 1s, transform .4s; +} + +.switch +{ + position: relative; + display: inline-block; + width: 40px; + height: 19px; +} +/* END switch */ + +#ayo_logo +{ + width: 200px; + fill: var(--main-fg-color); +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..827c342 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,60 @@ +`use strict` +import { Component } from 'inferno' +import { connect } from 'inferno-redux'; + +import { changeTheme } from './actions'; +import { Svg } from './svg.jsx' + +import './main.css' +import Logo from '../assets/logo.svg' + +class MainComponent extends Component +{ + componentDidMount() + { + // Smooth color transitions (after rendering to avoid flash on reload) + setTimeout(() => { + const styleEl = document.head.getElementsByTagName('style')[0] + document.head.appendChild(styleEl) + const styleSheet = styleEl.sheet + styleSheet.insertRule( + '*, ::after, ::before { transition-property: background-color, color, fill; transition-duration: 1s;}') + }, + 500) + } + + toggleTheme(event) + { + this.props.changeTheme(event.target.checked ? 'dark' : 'light') + } + + render() + { + return ( +
+ + +
) + } +} + +const mapStateToProps = state => ({ + theme: state.theme +}) + +const mapDispatchToProps = dispatch => ({ + changeTheme: theme => dispatch(changeTheme(theme)) +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(MainComponent) \ No newline at end of file diff --git a/src/reducers.js b/src/reducers.js new file mode 100644 index 0000000..393003c --- /dev/null +++ b/src/reducers.js @@ -0,0 +1,11 @@ +import { themeActions } from './actions.js' + +export const themeReducer = (state = 'light', action) => { + switch (action.type) + { + case themeActions.THEME_CHANGE: + return action.theme + default: + return state + } +} \ No newline at end of file diff --git a/src/svg.jsx b/src/svg.jsx new file mode 100644 index 0000000..819ffe1 --- /dev/null +++ b/src/svg.jsx @@ -0,0 +1,35 @@ +`use strict` +import { Component } from 'inferno' + +export class Svg extends Component +{ + constructor(props) + { + super(props) + this.state = { + svg: null, + loading: false + } + } + + componentDidMount() + { + fetch(this.props.url) + .then(res => res.text()) + .then(text => this.setState({ svg: text })) + } + + render() + { + const { loading, svg } = this.state; + if (loading) + { + return + } + else if(!svg) + { + return + } + return + } +} \ No newline at end of file diff --git a/webpack.base.js b/webpack.base.js new file mode 100644 index 0000000..d5e7a5d --- /dev/null +++ b/webpack.base.js @@ -0,0 +1,40 @@ +const path = require('path'); + +module.exports = { + mode: "none", + entry: './src/index.jsx', + module: + { + rules: [ + { + test: /\.jsx$/, + loader: "babel-loader", + options: + { + presets: ["@babel/preset-env"], + plugins: [ + ["babel-plugin-inferno", + { + "imports": true + }] + ] + } + }, + { + test: /src.*\.css$/, + use: ['style-loader', 'css-loader'] + }, + { + test: /assets.*\.(jpg|png|svg|css)$/, + loader: 'file-loader', + options: { + name: '[path][name].[ext]', + }, + }] + }, + output: + { + filename: 'bundle.js', + path: path.resolve(__dirname, 'public') + } +}; diff --git a/webpack.dev.js b/webpack.dev.js new file mode 100644 index 0000000..792c921 --- /dev/null +++ b/webpack.dev.js @@ -0,0 +1,61 @@ +const path = require('path'); +const webpack = require('webpack'); +const CompressionPlugin = require("compression-webpack-plugin") +const config = require('./webpack.base.js'); + +module.exports = Object.assign( + config, + { + mode: 'development', + devtool: 'source-map', // compile source map to get better debug output (error file/line) + cache: true, // caching already built files so unchanged files can be reuse when an other changes + watch: true, // watch file change and automatically rebuild the application + watchOptions: + { + ignored: /node_modules/ // avoid watching node_modules as it is usually huge, note that it can be useful when debugging packages + }, + resolve: + { + // avoid warning caused by development mode (using dev inferno build) + alias: + { + inferno: require.resolve('inferno/dist/index.dev.esm.js') + } + }, + plugins: [ + new webpack.DefinePlugin( + { + 'process.env': + { + 'DEBUG': true + } // set a DEBUG flag that can be used in the scripts + }), + new CompressionPlugin( + { + filename: "[path].gz[query]", + algorithm: "gzip", + test: /\.js/ + }) + ], + // webpack-dev-server configuration + devServer: + { + contentBase: path.join(__dirname), + compress: true, + port: 8080, + // Not elegant way to get rid of the gzip path in index.html + proxy: + { + '/$': + { + target: 'http://localhost:8080', + secure: false, + bypass: function(request, response, proxyOptions) + { + return '/index_dev.html'; + } + } + } + } + } +); diff --git a/webpack.prod.js b/webpack.prod.js new file mode 100644 index 0000000..ce853fb --- /dev/null +++ b/webpack.prod.js @@ -0,0 +1,29 @@ +const webpack = require('webpack'); +const CompressionPlugin = require("compression-webpack-plugin") +const config = require('./webpack.base.js'); + +module.exports = Object.assign( + config, + { + mode: "production", + optimization: + { + minimize: true + }, + plugins: [ + new webpack.DefinePlugin( + { + 'process.env': + { + 'DEBUG': false + } // set a DEBUG flag that can be used in the scripts (can be skipped) + }), + new CompressionPlugin( + { + filename: "[path].gz[query]", + algorithm: "gzip", + test: /\.js/ + }) + ] + } +);