[init] all

This commit is contained in:
xdrm-brackets 2018-03-21 18:44:27 +01:00
commit e5867beeb9
24 changed files with 970 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.vscode
.sass-cache
*.map
/node_modules
/public_html/css
/public_html/js
/package-lock.json

3
.htaccess Normal file
View File

@ -0,0 +1,3 @@
RewriteEngine on
RewriteRule ^(.*)$ public_html/$1 [QSA,L]

46
package.json Normal file
View File

@ -0,0 +1,46 @@
{
"name": "ptut-vhost",
"description": "PTUT",
"version": "1.0.0",
"author": "xdrm-brackets <xdrm.brackets.dev@gmail.com> SeekDaSky <mascaro.lucas@yahoo.fr G. Fauvet <gfauvet@gmail.com>",
"license": "MIT",
"private": true,
"scripts": {
"bundle:clean": "exit 0",
"bundle:prod": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"bundle:dev": "cross-env NODE_ENV=development webpack --progress --hide-modules",
"bundle:watch": "cross-env NODE_ENV=development webpack --progress --watch --hide-modules",
"scss": "node-sass -r --output-style compressed --output ./public_html/css ./webpack/scss",
"watch-css": "node-sass -w -r --output-style compressed --output ./public_html/css ./webpack/scss",
"dev": "npm run bundle:clean; npm run bundle:dev; npm run watch-css",
"devjs": "npm run bundle:clean; npm run bundle:watch",
"build": "npm run bundle:clean; npm run bundle:prod; npm run scss"
},
"dependencies": {
"uglifyjs-webpack-plugin": "^1.2.3",
"vue": "^2.5.9",
"vue-router": "^2.5.3"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"cross-env": "^5.0.5",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.4",
"node-sass": "^4.7.2",
"sass-loader": "^6.0.6",
"vue-loader": "^13.0.5",
"vue-svg-loader": "^0.5.0",
"vue-template-compiler": "^2.5.9",
"webpack": "^3.8.1",
"webpack-dev-server": "^2.9.5"
}
}

4
public_html/.htaccess Normal file
View File

@ -0,0 +1,4 @@
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=/$1 [QSA,L]

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 100 100"
id="Layer_1"
version="1.1"
viewBox="0 0 100 100"
xml:space="preserve"
sodipodi:docname="menu.add.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview5"
showgrid="false"
inkscape:zoom="13.350176"
inkscape:cx="57.025698"
inkscape:cy="50.871048"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#6b6e6a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 48.400391,22.099609 v 29 h -29 v 3.5 h 29 v 29 h 3.5 v -29 h 29 v -3.5 H 80.199219 51.900391 v -29 z"
id="polygon2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccc" /></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 100 100"
id="Layer_1"
version="1.1"
viewBox="0 0 100 100"
xml:space="preserve"
sodipodi:docname="menu.add@hover.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview5"
showgrid="false"
inkscape:zoom="13.350176"
inkscape:cx="57.025698"
inkscape:cy="50.871048"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#dddddd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 48.400391,22.099609 v 29 h -29 v 3.5 h 29 v 29 h 3.5 v -29 h 29 v -3.5 H 80.199219 51.900391 v -29 z"
id="polygon2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccc" /></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-289 382 32 30" style="enable-background:new -289 382 32 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{opacity:0.6;}
</style>
<path class="st0" d="M-273,409.5L-273,409.5c-4.1,0-6.8-0.6-7.9-1.7c-0.5-0.6-0.6-1.1-0.6-1.3c0-0.7,0.1-2.9,0.6-3.8 c0.1-0.3,0.5-1,4.5-2.4c-1.6-1.4-2.6-4-2.6-7.1c0-4.2,2.3-7,5.9-7.1l0.1,0c3.6,0.1,5.9,2.8,5.9,7.1c0,3.1-1,5.7-2.6,7.1 c4,1.4,4.4,2.1,4.5,2.4c0.4,0.9,0.5,3.1,0.6,3.8c0,0.2,0,0.7-0.6,1.3C-266.3,408.9-268.9,409.5-273,409.5z M-273,407.5L-273,407.5 c5.1,0,6.2-0.9,6.4-1.1c-0.1-1.1-0.2-2.3-0.3-2.7c-0.6-0.4-2.9-1.3-4.8-1.9l-0.7-0.2l-0.1-2l0.7-0.3c1.7-0.6,2.8-3.1,2.8-6.1 c0-3.1-1.5-5-3.9-5.1c-2.5,0-4,2-4,5.1c0,3,1.1,5.5,2.8,6.1l0.7,0.3l-0.1,2l-0.7,0.2c-1.9,0.6-4.2,1.5-4.8,1.9 c-0.1,0.4-0.3,1.6-0.3,2.7C-279.2,406.6-278,407.5-273,407.5z"/>
<g class="st1">
<path class="st0" d="M-257,402.8c0-0.7-0.1-2.9-0.6-3.8c-0.1-0.3-0.5-1-4.5-2.4c1.6-1.4,2.6-4,2.6-7.1c0-4.2-2.3-7-5.9-7.1l-0.1,0 c-1.9,0-3.5,0.8-4.5,2.2c0.6,0.3,1.2,0.6,1.8,1c0.7-0.8,1.6-1.3,2.8-1.3c2.4,0,3.9,2,3.9,5.1c0,3-1.1,5.5-2.8,6.1l-0.7,0.3l0.1,2 l0.7,0.2c1.9,0.6,4.3,1.5,4.8,1.9c0.1,0.4,0.3,1.6,0.3,2.7c-0.2,0.2-1,0.8-3.8,1c0.1,0.6,0.2,1.2,0.2,2c2.5-0.2,4.2-0.8,5-1.6 C-257,403.5-257,403-257,402.8z"/>
<path class="st0" d="M-287,402.7c0.1-1.1,0.2-2.3,0.3-2.7c0.6-0.4,2.9-1.3,4.8-1.9l0.7-0.2l0.1-2l-0.7-0.3 c-1.6-0.6-2.8-3.1-2.8-6.1c0-3.1,1.5-5,4-5.1c1.2,0,2.1,0.5,2.8,1.3c0.5-0.4,1.1-0.8,1.8-1c-1-1.4-2.6-2.2-4.5-2.2l-0.1,0 c-3.6,0-5.9,2.8-5.9,7.1c0,3.1,1,5.7,2.6,7.1c-4,1.4-4.4,2.1-4.5,2.4c-0.4,0.9-0.5,3.1-0.6,3.8c0,0.2,0,0.7,0.6,1.3 c0.8,0.9,2.5,1.4,5.1,1.6c0-0.7,0.1-1.4,0.2-2C-286,403.5-286.8,402.9-287,402.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

33
public_html/index.html Normal file
View File

@ -0,0 +1,33 @@
<html>
<!-- HEADER -->
<head>
<title>xdrm-brackets</title>
<!-- META -->
<meta charset='utf-8'>
<meta name='author' content='xdrm-brackets (Adrien Marquès)'>
<meta name='description' content='[Home] Home page'>
<!-- STYLESHEET -->
<link type='text/css' rel='stylesheet' href='./css/layout.css'>
<link type='text/css' rel='stylesheet' href='./css/menu.css'>
<link type='text/css' rel='stylesheet' href='./css/dialog.css'>
<link type='text/css' rel='stylesheet' href='./css/container.css'>
<!-- FONT -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro|Arvo|Exo+2" rel="stylesheet">
<!-- BODY -->
</head>
<body>
<div id='vue'></div>
<script type='text/javascript' src='./js/bundle.js'></script>
</body>
</html>

85
webpack.config.js Normal file
View File

@ -0,0 +1,85 @@
var path = require('path');
var webpack = require('webpack');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const extractSass = new ExtractTextPlugin({
filename: "[name].css",
disable: process.env.NODE_ENV === "development"
});
var mod_common = {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {} // other vue-loader options go here
}
}, {
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}, {
test: /\.(png|jpg|gif)$/,
loader: 'file-loader',
options: { name: '[name].[ext]?[hash]' }
}, {
test: /\.svg$/,
loader: 'vue-svg-loader', // `vue-svg` for webpack 1.x
options: {
// optional [svgo](https://github.com/svg/svgo) options
svgo: {
plugins: [
{removeDoctype: true},
{removeComments: true}
]
}
}
}
]
};
module.exports = {
name: "main",
entry: './webpack/main.js',
output: {
path: path.resolve(__dirname, './public_html/js'),
publicPath: '/js/',
filename: 'bundle.js'
},
module: mod_common,
devtool: (process.env.NODE_ENV==='development') ? '#eval-source-map' : false
}
if (process.env.NODE_ENV === 'production') {
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new UglifyJSPlugin({
sourceMap: true
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

40
webpack/common.js Normal file
View File

@ -0,0 +1,40 @@
import {GlobalStore} from './lib/gstore'
import VueRouter from 'vue-router'
import routes from './routes'
window.gs = new GlobalStore();
/* (1) Global data
---------------------------------------------------------*/
/* (1) Get Full URI */
gs.set('URI', document.URL.replace(/^(?:[^\/]+\/\/|[^\/]+\/)/, '').split('/').filter(function(v,i){ return !!i && v.length; }));
/* (2) Store routes */
gs.set('routes', routes[0]);
/* (3) Init. vue router */
gs.set('router', new VueRouter({
routes: gs.get.routes
}));
/* (2) Main components
---------------------------------------------------------*/
/* (1) Menu - channel list */
gs.set('channel', {
list: {
me: { label: '0 online', icon: 'group' },
test1: { label: null, icon: '' },
test2: { label: null, icon: '' },
add: { label: null, icon: 'add', add: 1 }
},
active: 'me'
});
/* (2) Set current active menu item from URL */
if( gs.get.URI.length > 1 && gs.get.channel.list.hasOwnProperty(gs.get.URI[gs.get.URI.length-1]) )
gs.get.channel.active = gs.get.URI[gs.get.URI.length-1];

161
webpack/lib/api-client.js Normal file
View File

@ -0,0 +1,161 @@
/* classe API */
export class APIClient{
constructor(target){
this.target = target;
this.xhr = []; // tableau d'objets pour les requêtes ajax
this.buffer = null; // Contiendra le buffer pour debugger si erreur de parsage
this.error = { // error constants
'-1': 'Invalid target format: "METHOD module/method"',
'-2': 'XHR error',
'-3': 'Invalid JSON response',
};
}
/* transaction avec le serveur (http://{host}/api/)
*
* @param pTarget<String> URI cible, format "HTTP_METHOD uri/uri/uri"
* @param pArgs<Object> Le tableu d'arguments passé en POST (attribut<->postfield) à http://{host}/api/
* @param pHandler<Function> Fonction qui s'éxécutera lors de la réponse (1 argument -> réponse<Object>)
* @param pToken<String> Optionnel, token d'auth pour l'api
*
***************************************************************************************************
*
* @usecase
* 1. api.call(
* 2. "PUT newspaper/article/4"
* 2. { content: "New article content" },
* 3. function(resp){
* 4. alert(resp.error);
* 5. }
* 6. );
*
*/
call(pTarget, pArgs, pHandler, pToken=null){
/* (1) Check @pHandler (for dispatching errors)
---------------------------------------------------------*/
/* (1) Check if is a Function */
if( !(pHandler instanceof Function) )
throw new Error("3rd argument must be a function, but is of type '"+typeof(pHandler)+"' !");
/* (2) Check @pTarget
---------------------------------------------------------*/
/* (1) Check format */
if( !/^([A-Z]+) (.+)/i.test(pTarget) ){
pHandler({ error: -1, ErrorDescription: this.error['-1'] });
return false;
}
/* (2) Store locally data */
var lHttpMethod = RegExp.$1;
var lUri = RegExp.$2;
/* (3) Create form data
---------------------------------------------------------*/
/* (1) Create virtual form */
var lForm = new FormData();
/* (2) Add attributes */
for( var key in pArgs ){
// {2.1} If a file -> send as it //
if( pArgs[key] instanceof File )
lForm.append(key, pArgs[key]);
// {2.2} Else -> JSON stringify //
else
lForm.append(key, JSON.stringify(pArgs[key]));
}
/* (4) Create XHR request
---------------------------------------------------------*/
/* (1) Clean ended requests */
for( var i = this.xhr.length-1 ; i >= 0 ; i-- ){
if( this.xhr[i] != null )
break;
this.xhr.pop();
}
/* (2) Push a new entry -> fetch its index */
i = this.xhr.push(null) - 1;
/* (3) Create XHR object */
this.xhr[i] = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttpRequest');
/* (5) Bind response event
---------------------------------------------------------*/
var self = this; // to access the buffer
this.xhr[i].onreadystatechange = function(i){
/* (1) If request over */
if( this.xhr[i].readyState == 4 ){
/* (2) Update buffer (for debug) */
self.buffer = this.xhr[i].responseText;
/* (3) If request success */
if( [0, 200, 417].indexOf(this.xhr[i].status) > -1 ){
/* (3.1) Create default response (if JSON error) */
var response = {error:-3, ErrorDescription: self.error['-3']};
/* (3.2) Try to parse JSON */
try{ response = JSON.parse(this.xhr[i].responseText); }catch(e){}
/* (3.3) Launch @pHandler with response */
pHandler(response);
/* (4) If request error */
}else
pHandler({ error:-2, ErrorDescription: self.error['-2'] });
/* (5) Notify current xhr instance is done */
this.xhr[i] = null;
}
}.bind(this, i);
/* (6) Finish & send request
---------------------------------------------------------*/
/* (1) Open the XHR */
this.xhr[i].open(lHttpMethod, this.target+lUri, true);
/* (2) Manage optional token */
if( pToken != null )
this.xhr[i].setRequestHeader('Authorization', 'Digest '+pToken);
/* (3) Custom header to notify we're using Ajax */
this.xhr[i].setRequestHeader('X-Requested-With', 'XMLHttpRequest');
/* (4) Make the call */
this.xhr[i].send( lForm );
return true;
}
}

14
webpack/lib/gstore.js Normal file
View File

@ -0,0 +1,14 @@
/* GlobalStore for VueJS */
export class GlobalStore{
constructor(){
this.get = {};
}
set(field, value){
this.get[field] = value;
}
}

25
webpack/main.js Normal file
View File

@ -0,0 +1,25 @@
/* (1) Imports
---------------------------------------------------------*/
/* (1) NPM libs */
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
/* (2) Vues */
import wrapper from './vue/wrapper.vue'
/* (3) Data */
require('./common.js');
/* (2) Initialisation
---------------------------------------------------------*/
/* (1) Render view */
Vue.use(VueRouter);
new Vue({
el: '#vue',
router: gs.get.router,
render(h){ return h(wrapper); }
})

35
webpack/page/home.js Normal file
View File

@ -0,0 +1,35 @@
/* (1) Imports
---------------------------------------------------------*/
/* (1) NPM libs */
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from '../routes/home'
/* (2) Vues */
import wrapper_vue from '../vue/wrapper.vue'
/* (3) Data */
require('../data/common');
require('../data/home');
/* (2) Initialisation
---------------------------------------------------------*/
/* (1) Init Router */
const router = new VueRouter({
mode: 'history',
routes: require('./routes.js')
});
/* (2) Store router in gstore */
gstore.add('router', router);
/* (3) Render view */
Vue.use(VueRouter);
new Vue({
el: '#WRAPPER',
router,
render: h => h(wrapper_vue)
});

11
webpack/routes.js Normal file
View File

@ -0,0 +1,11 @@
export default{ 0: [
{
path: '/channel/:id',
component: require('./vue/channel.vue').default
}, {
path: '*',
redirect: '/channel/me'
}
] }

View File

@ -0,0 +1,30 @@
/* (1) Dimensions */
$menu-width: 4.3em;
$dialog-width: 15.2em;
$header-height: 3em;
/* (2) Main colors */
$menu-bg: #202225;
$dialog-header-bg: #2f3136;
$dialog-bg: #2f3136;
$container-bg: #36393e;
$header-bg: #36393f;
/* (1) Header specific
---------------------------------------------------------*/
/* (1) Header border-bottom */
$bb-height: 1px;
$bb-offset: 2px;
/* (2) Menu Specific
---------------------------------------------------------*/
/* (1) Menu item size */
$menu-item-size: #{$menu-width * .72};
$menu-item-space: .3em;
/* (2) Default menu item background */
$menu-item-bg: #2f3136;

View File

@ -0,0 +1,45 @@
@import 'constants';
#WRAPPER > div.container{
display: block;
position: absolute;
top: 0;
left: #{$menu-width + $dialog-width};
width: calc( 100% - #{$menu-width + $dialog-width} );
height: 100%;
background-color: $container-bg;
/* (1) Container HEADER */
& > div.header{
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: $header-height;
border-bottom: #{$bb-height} solid darken($container-bg, 5%);
box-shadow: 0 #{$bb-offset - $bb-height} 0 darken($container-bg, 2%);
z-index: 200;
}
/* (2) Container BODY */
& > div.body{
display: block;
position: absolute;
top: calc( #{$header-height} + #{$bb-offset} );
left: 0;
width: 100%;
height: calc( 100% - #{$header-height} - #{$bb-offset} );
background-color: $container-bg;
z-index: 100;
}
}

44
webpack/scss/dialog.scss Normal file
View File

@ -0,0 +1,44 @@
@import 'constants';
#WRAPPER > div.dialog{
display: block;
position: absolute;
top: 0;
left: $menu-width;
width: $dialog-width;
height: 100%;
background-color: $dialog-bg;
/* (1) Container HEADER */
& > div.header{
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: $header-height;
border-bottom: #{$bb-height} solid darken($dialog-header-bg, 5%);
box-shadow: 0 #{$bb-offset - $bb-height} 0 darken($dialog-header-bg, 2%);
z-index: 200;
}
/* (2) Container BODY */
& > div.body{
display: block;
position: absolute;
top: calc( #{$header-height} + #{$bb-offset} );
left: 0;
width: 100%;
height: calc( 100% - #{$header-height} - #{$bb-offset} );
background-color: $dialog-bg;
z-index: 100;
}
}

45
webpack/scss/layout.scss Normal file
View File

@ -0,0 +1,45 @@
*{
margin: 0;
padding: 0;
}
body{
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
color: #fff;
font-family: 'Source Sans Pro';
font-size: 16px;
& > #WRAPPER{
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f00;
}
}

119
webpack/scss/menu.scss Normal file
View File

@ -0,0 +1,119 @@
@import 'constants';
#WRAPPER > div.menu{
display: flex;
position: absolute;
top: 0;
left: 0;
width: $menu-width;
height: 100%;
background-color: $menu-bg;
flex-direction: column;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
overflow: hidden;
/* (1) Menu Item */
& > span.channel{
display: block;
position: relative;
width: $menu-item-size;
height: $menu-item-size;
margin: #{$menu-item-space} 0;
border-radius: 50% / 50%;
background: $menu-item-bg center center no-repeat;
background-size: auto 55%;
overflow: hidden;
cursor: pointer;
transition: border-radius .4s cubic-bezier(.49, .4, .26, 1.93),
background-color .4s ease-in-out,
border-color .2s ease-in-out;
// {1} First item have margin-top //
&:first-child{
margin-top: #{$menu-item-space * 1.8};
}
// {2} [data-add] have no background color //
&[data-add]{
background-color: #1e2124;
border: 1px dashed #535552;
&:hover{ border-color: #ddd; }
}
// {3} Hover + current channel (except [data-add])//
&:not([data-add='1']):hover,
&:not([data-add='1']).active{
border-radius: 30% / 30%;
// if [data-special=1] -> colorize
&[data-special='1']{ background-color: #7289da; }
}
// {4} sub label //
&[data-sub]:not([data-sub='']){
display: inline-block;
position: relative;
margin-bottom: #{$menu-item-space + 2em};
overflow: visible;
// show sub-text
&:before{
content: attr(data-sub);
display: inline-block;
position: absolute;
top: calc( 100% + 1em );
left: 1em;
width: calc( 100% - 2em );
height: calc( 2em + #{$menu-item-space} );
font-size: .6em;
color: #777;
text-indent: -.7em;
text-align: center;
white-space: nowrap;
text-transform: uppercase;
border-bottom: 2px solid #{$menu-item-bg};
overflow: visible;
}
}
// {5} Specific icons //
&[data-icon='group']{
background-image: url('../asset/svg/menu.group.svg');
}
&[data-icon='add']{
background-image: url('../asset/svg/menu.add.svg');
&:hover{ background-image: url('../asset/svg/menu.add@hover.svg'); }
}
}
}

18
webpack/vue/channel.vue Normal file
View File

@ -0,0 +1,18 @@
<template>
<div class='body'>
</div>
</template><script>
export default {
name: 'channel-',
data(){ return { gs: gs.get }; }
}
</script>

28
webpack/vue/dialog.vue Normal file
View File

@ -0,0 +1,28 @@
<template>
<div class='dialog'>
<div class='header'>
</div>
<div class='body'>
</div>
</div>
</template><script>
export default {
name: 'dialog-',
data(){ return { gs: gs.get }; },
}
</script>

33
webpack/vue/menu.vue Normal file
View File

@ -0,0 +1,33 @@
<template>
<div class='menu'>
<!-- First elements -->
<!-- Channel List -->
<span v-for='(c,link) in gs.channel.list'
@click='$router.push(`/channel/${link}`); gs.channel.active=link'
:class='link == gs.channel.active ? `channel active` : `channel`'
:data-sub='c.label'
:data-special='link == `me`?1:0'
:data-add='c.add'
:data-icon='c.icon'
>{{ c.name }}</span>
<!-- Last elements -->
</div>
</template><script>
export default {
name: 'menu-',
data(){ return { gs: gs.get }; }
}
</script>

43
webpack/vue/wrapper.vue Normal file
View File

@ -0,0 +1,43 @@
<template>
<div id="WRAPPER">
<!-- Side Menu -->
<menu-comp></menu-comp>
<!-- Side Dialog -->
<dialog-comp></dialog-comp>
<!-- Container -->
<div class='container'>
<div class='header'></div>
<router-view></router-view>
</div>
</div>
</template><script>
import menu_vue from './menu.vue'
import dialog_vue from './dialog.vue'
export default {
name: 'wrapper-',
data(){ return { gs: gs.get }; },
components: {
'MenuComp': menu_vue,
'DialogComp': dialog_vue
}
}
</script>