Base for self contained ui applications (vuejs+typescript) WIP #2

Manually merged
sagi merged 1 commit from typescript into master 2020-01-26 22:17:53 +00:00
33 changed files with 648 additions and 39 deletions

3
.gitignore vendored
View file

@ -14,3 +14,6 @@ database/*.sqlite
# VSCode & Webstorm history directories
.history
.idea
#@sagi
helper-junk/

View file

@ -5,10 +5,12 @@
"description": "A shared story time experience",
"main": "server.js",
"scripts": {
"css-build": "node_modules/.bin/node-sass --omit-source-map-url resources/sass/main.scss public/style.css",
"css-watch": "npm run css-build -- --watch",
"build:css": "node_modules/.bin/node-sass --omit-source-map-url resources/sass/main.scss public/style.css",
"build:applications": "node_modules/.bin/webpack --mode production",
"watch:css": "npm run build:css -- --watch",
"watch:applications": "node_modules/.bin/webpack --watch",
"migrate": "node_modules/.bin/adonis migration:run -f",
"build": "npm run migrate && npm run css-build",
"build": "npm run migrate && npm run build:css && npm run build:applications",
"start": "npm run migrate && node server.js",
"test": "node ace test"
},
@ -33,12 +35,25 @@
"@adonisjs/validator": "^5.0.6",
"bulma": "^0.8.0",
"fork-awesome": "^1.1.7",
"sqlite3": "^4.1.1"
"sqlite3": "^4.1.1",
"typescript": "^3.7.5",
"vue": "^2.6.11"
},
"devDependencies": {
"node-sass": "^4.13.0"
"@babel/core": "^7.8.3",
"babel-loader": "^8.0.6",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
"css-loader": "^3.4.2",
"node-sass": "^4.13.0",
"ts-loader": "^6.2.1",
"vue-loader": "^15.8.3",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
},
"autoload": {
"App": "./app"
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,11 @@
"use strict";
document.addEventListener("DOMContentLoaded", function () {
(function Navbar() {
var menuButton = document.getElementById('menu-button');
var navMenu = document.getElementById('nav-menu');
menuButton.onclick = function (event) {
navMenu.classList.toggle('is-active');
};
})();
});
//# sourceMappingURL=navbar.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"navbar.js","sourceRoot":"","sources":["../../../resources/scripts/components/navbar.ts"],"names":[],"mappings":";AACA,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE;IAC5C,CAAC,SAAS,MAAM;QACd,IAAM,UAAU,GAAsB,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAsB,CAAC;QAClG,IAAM,OAAO,GAAmB,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAmB,CAAC;QAEtF,UAAU,CAAC,OAAO,GAAG,UAAC,KAAK;YACzB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACxC,CAAC,CAAA;IAGH,CAAC,CAAC,EAAE,CAAC;AAEP,CAAC,CAAC,CAAC"}

View file

@ -6871,3 +6871,25 @@ label.panel-block {
.p-l-xxl {
padding-left: 8rem; }
.app .columns {
margin-bottom: 0px !important;
max-height: calc(100vh - 12.25rem);
height: calc(100vh - 12.25rem); }
.sidebar {
flex-direction: column; }
.is-sidebar {
border-right: thin #ccc solid;
max-width: 5em;
width: 5em;
transform: translate(15%, 0); }
.sidebar-menu {
position: absolute;
bottom: 0px; }
.sideway-letter {
transform: rotate(-90deg);
margin: -.8em auto; }

View file

@ -172,4 +172,42 @@ $positions: (
padding-#{$posValue}: sizeValue($sizeKey, $sizeValue);
}
}
}
}
.app {
.columns{
margin-bottom: 0px !important;
max-height: calc(100vh - 12.25rem);
height: calc(100vh - 12.25rem);
}
}
.app-content{
// height: 100vh;
}
.sidebar{
// margin: auto;
flex-direction: column;
// color: $beige-lighter;
}
.is-sidebar {
border-right: thin #ccc solid;
max-width: 5em;
width: 5em;
transform: translate(15%, 0);
// background-color: rgba(0, 0, 0, .3);
// background-color:rgba(127, 88, 145, 0.7);
}
.sidebar-menu{
position: absolute;
bottom: 0px;
}
.sideway-text{
// transform: translate(0, -50%);
// display: inline;
// margin: 0 0;
}
.sideway-letter{
// width: 50%;
// height: 2em;
transform: rotate(-90deg);
margin: -.8em auto;
}

View file

@ -0,0 +1,43 @@
import Header from "./components/Header";
import {
default as SideBar,
IMenuItem
} from "../shared/components/SideBar/SideBar";
const menu: IMenuItem[] = [
{
href: '#',
text: 'Start a session',
isActive: false,
icon: 'fa fa-bookmark'
},
{
href: '#',
text: 'Create something',
isActive: true,
icon: 'fa fa-book',
},
{
href: '/logout',
text: 'Logout',
icon: 'fa fa-gears',
isActive: false
},
{
href: '/logout',
text: 'Logout',
icon: 'fa fa-sign-out',
isActive: false
}
]
export default {
name: "app",
components: {
SideBar,
Header
},
data: () => ({
appName: "Home",
menu
})
};

View file

@ -0,0 +1,61 @@
<template>
<div class="app">
<Header :appName="appName" />
<div class="columns m-t-xs">
<div class="column is-sidebar">
<SideBar :title="appName" :menu="menu" selectedItem="Your Dashboard" :appName="appName" />
</div>
<div class="section column app-content">
<!-- start shit -->
<div class="tile is-ancestor">
<div class="tile is-vertical is-8">
<div class="tile">
<div class="tile is-parent is-vertical">
<article class="tile is-child notification is-primary">
<p class="title">Vertical...</p>
<p class="subtitle">Top tile</p>
</article>
<article class="tile is-child notification is-warning">
<p class="title">...tiles</p>
<p class="subtitle">Bottom tile</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child notification is-info">
<p class="title">Middle tile</p>
<p class="subtitle">With an image</p>
<figure class="image is-4by3">
<img src="https://bulma.io/images/placeholders/640x480.png" />
</figure>
</article>
</div>
</div>
<div class="tile is-parent">
<article class="tile is-child notification is-danger">
<p class="title">Wide tile</p>
<p class="subtitle">Aligned with the right tile</p>
<div class="content">
<!-- Content -->
</div>
</article>
</div>
</div>
<div class="tile is-parent">
<article class="tile is-child notification is-success">
<div class="content">
<p class="title">Tall tile</p>
<p class="subtitle">With even more content</p>
<div class="content">
<!-- Content -->
</div>
</div>
</article>
</div>
</div>
<!-- end shit -->
</div>
</div>
</div>
</template>
<script lang="ts" src="./app.ts"/>

View file

@ -0,0 +1,4 @@
export default {
name: "Header",
props: ['appName']
}

View file

@ -0,0 +1,12 @@
<template>
<section class="hero is-small is-primary hero-bg-landing01">
<div class="hero-body">
<div class="container">
<h1 class="title">{{appName}}</h1>
<h2 class="subtitle">Test</h2>
</div>
</div>
</section>
</template>
<script lang="ts" src="./Header.ts">
</script>

View file

@ -0,0 +1,14 @@
// import Vue from 'vue';
// import App from './app.vue';
// new Vue({
// render: h => h(App)
// }).$mount('#app')
<script>
import Vue from "vue";
import App from "./app.vue";
new Vue({
render: h => h(App)
}).$mount("#app");
</script>

View file

@ -0,0 +1,15 @@
import SidewayText from '../SidewayText/SidewayText';
export interface IMenuItem {
href: string;
text: string;
icon: string;
isActive: boolean;
}
export default {
components: {
SidewayText
},
name: "SideMenu",
props: ["menu", "title", "appName", "selectedItem"]
};

View file

@ -0,0 +1,19 @@
<template>
<div class="sidebar has-text-centered">
<div class="has-text-centered">
<SidewayText textSize="is-size-6" :text="appName" :bold="true" />
<SidewayText class="is-size-6" :text="selectedItem" />
<!-- <div class="is-size-6 has-text-weight-bold m-l-lg m-r-md">{{appName}}</div> -->
</div>
<aside class="menu is-primary p-xxs sidebar-menu">
<ul class="menu-list">
<li v-for="item in menu" class="m-t-md m-b-md">
<a :href="item.href" :class="['button',{ 'is-active': !!item.isActive }]">
<i :class="['icon', item.icon]"></i>
</a>
</li>
</ul>
</aside>
</div>
</template>
<script lang="ts" src="./SideBar.ts" />

View file

@ -0,0 +1,9 @@
export interface ISidewayText {
class: string;
}
export default {
props: ["text", "bold", "textSize"],
data: () => ({
}),
};

View file

@ -0,0 +1,16 @@
<template>
<div class="sideway-text m-b-lg m-t-lg">
<div
v-for="letter in text.split('').slice().reverse()"
:class="[{'has-text-weight-bold':bold}, textSize, 'sideway-letter', 'has-text-centered']"
>{{letter === ' ' ? '&nbsp;': letter}}</div>
</div>
<!-- <div class="sideway-text">
<div class="continer sideway-letter-container" >
<div class="container sideway-letter">{{letter}}</div>
</div>
</div>-->
</template>
<script lang="ts" src="./SidewayText.ts" />

View file

@ -0,0 +1,43 @@
import Header from "./components/Header";
import {
default as SideBar,
IMenuItem
} from "../shared/components/SideBar/SideBar";
const menu: IMenuItem[] = [
{
href: '#',
text: 'Start a session',
isActive: false,
icon: 'fa fa-bookmark'
},
{
href: '#',
text: 'Create something',
isActive: true,
icon: 'fa fa-book',
},
{
href: '/logout',
text: 'Logout',
icon: 'fa fa-gears',
isActive: false
},
{
href: '/logout',
text: 'Logout',
icon: 'fa fa-sign-out',
isActive: false
}
]
export default {
name: "app",
components: {
SideBar,
Header
},
data: () => ({
appName: "Story Time",
menu
})
};

View file

@ -0,0 +1,61 @@
<template>
<div class="app">
<Header username="sagi" />
<div class="columns m-t-xs">
<div class="column is-sidebar">
<SideBar :title="appName" :menu="menu" selectedItem="Test" :appName="appName" />
</div>
<div class="section column app-content">
<!-- start shit -->
<div class="tile is-ancestor">
<div class="tile is-vertical is-8">
<div class="tile">
<div class="tile is-parent is-vertical">
<article class="tile is-child notification is-primary">
<p class="title">Vertical...</p>
<p class="subtitle">Top tile</p>
</article>
<article class="tile is-child notification is-warning">
<p class="title">...tiles</p>
<p class="subtitle">Bottom tile</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child notification is-info">
<p class="title">Middle tile</p>
<p class="subtitle">With an image</p>
<figure class="image is-4by3">
<img src="https://bulma.io/images/placeholders/640x480.png" />
</figure>
</article>
</div>
</div>
<div class="tile is-parent">
<article class="tile is-child notification is-danger">
<p class="title">Wide tile</p>
<p class="subtitle">Aligned with the right tile</p>
<div class="content">
<!-- Content -->
</div>
</article>
</div>
</div>
<div class="tile is-parent">
<article class="tile is-child notification is-success">
<div class="content">
<p class="title">Tall tile</p>
<p class="subtitle">With even more content</p>
<div class="content">
<!-- Content -->
</div>
</div>
</article>
</div>
</div>
<!-- end shit -->
</div>
</div>
</div>
</template>
<script lang="ts" src="./app.ts"/>

View file

@ -0,0 +1,4 @@
export default {
name: "Header",
props: ['username']
}

View file

@ -0,0 +1,12 @@
<template>
<section class="hero is-small is-primary hero-bg-landing01">
<div class="hero-body">
<div class="container">
<h1 class="title">{{username}} Story Time</h1>
<h2 class="subtitle">Test</h2>
</div>
</div>
</section>
</template>
<script lang="ts" src="./Header.ts">
</script>

View file

@ -0,0 +1,16 @@
// import Vue from 'vue';
// import App from './app.vue';
// new Vue({
// render: h => h(App)
// }).$mount('#app')
<script>
import Vue from 'vue';
import App from './app.vue';
new Vue({
render: h => h(App)
}).$mount('#app')
</script>

View file

@ -0,0 +1,14 @@
document.addEventListener("DOMContentLoaded", function () {
(function Navbar() {
const menuButton: HTMLAnchorElement = document.getElementById('menu-button') as HTMLAnchorElement;
const navMenu: HTMLDivElement = document.getElementById('nav-menu') as HTMLDivElement;
menuButton.onclick = (event) => {
navMenu.classList.toggle('is-active');
}
})();
//....
});

View file

@ -0,0 +1,10 @@
@layout('layouts.application');
@section('content')
<div id="app">
Loading...
</div>
{{ script('scripts/applications/story-time/story-time.bundle.js') }}
@endsection

View file

@ -1,36 +1,37 @@
{{ script('scripts/components/navbar.js') }}
<nav class="{{ isLanding ? 'darken' : '' }} navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/">
{{-- <img src="https://bulma.io/images/bulma-logo.png" width="112" height="28"> --}}
<strong>Seepur</strong>
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
<a id="menu-button" role="button" class="navbar-burger burger {{ isLanding ? 'has-text-light' : '' }} " aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="navbar-menu">
<div id="nav-menu" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item">
Home
</a>
<a class="navbar-item">
About
</a>
</div>
<div class="navbar-end">
@loggedIn
<div class="navbar-item has-dropdown is-hoverable is-dark">
<a class="navbar-link">
{{auth.user.name}}
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
Settings
@ -50,8 +51,8 @@
Log in
</a>
</div>
</div>
</div>
@endloggedIn
</div>
</div>
</nav>
</nav>

View file

@ -1,10 +1,9 @@
@layout('layouts.main')
@layout('layouts.application');
@section('page-title')
Seepur | Home
@endsection
@section('content')
<h1 class="title">{{auth.user.name}}</h1>
<h2 class="subtitle">{{auth.user.email}}</h2>
@endsection
<div id="app">
Loading...
</div>
{{ script('scripts/applications/home/home.bundle.js') }}
@endsection

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@include('partials.SEO.meta')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous">
<title>
@!section('page-title')
</title>
{{ style('style') }}
@!section('scripts')
</head>
<body>
@!component('components.nav.nav', isLanding = false, auth=auth)
@!section('hero')
<div class="">
@!section('content')
</div>
@include('partials.footer')
</body>
</html>

View file

@ -40,8 +40,8 @@
</div>
<p class="help is-danger">{{ getErrorFor('name') ? getErrorFor('name') : '' }}</p>
</div>
<div class="field">
<label class="label has-text-light">Email</label>
<div class="control has-icons-left">
@ -49,11 +49,11 @@
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('email') ? getErrorFor('email') : '' }}</p>
</div>
<div class="field">
<label class="label has-text-light">Password</label>
<div class="control has-icons-left">
@ -88,17 +88,10 @@
</div>
</div>
</nav>
</form>
</section>
</div>
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>Seepur</strong> | The source code is licensed
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. Made with <i class="fa fa-heart"></i> by friends and family. For all the savtot out there
</p>
</div>
</footer>
@include('partials.footer')
</body>
</html>
</html>

View file

@ -23,5 +23,6 @@
</div>
</section>
@include('partials.footer')
</body>
</html>
</html>

View file

@ -0,0 +1,8 @@
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>Seepur</strong> | The source code is licensed
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. Made with <i class="fa fa-heart"></i> by friends and family. For all the savtot out there
</p>
</div>
</footer>

View file

@ -27,3 +27,12 @@ Route.get('/login', 'AuthController.loginIndex').as('login');
Route.post('/register', 'AuthController.register').validator('Register');
Route.post('/login', 'AuthController.login').validator('Login');
/*
/ Applications
*/
Route
.get(
'/applications/story-time',
({view}) => view.render('applications.story-time.app'))
.middleware(['auth']);

17
tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "es2015",
"moduleResolution": "node",
"target": "es5",
"sourceMap": true,
"allowJs": true,
"experimentalDecorators": true
},
"include": [
"resources/scripts/applications/story-time/app.ts"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}

99
webpack.config.js Normal file
View file

@ -0,0 +1,99 @@
const path = require('path');
const webpack = require('webpack');
// const HtmlWebpackPlugin = require('html-webpack-plugin');
// const CleanWebpackPlugin = require('clean-webpack-plugin');
// const CopyWebpackPlugin = require('copy-webpack-plugin');
const {VueLoaderPlugin} = require('vue-loader')
require('babel-loader');
require('ts-loader');
const resolve = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
mode: 'development',
entry: {
'story-time': './resources/scripts/applications/story-time/main.vue',
'home': './resources/scripts/applications/home/main.vue',
// App2: './App2/main.js'
},
// output: {
// filename: '[name].js',
// // Folder where the output of webpack's result go.
// path: resolve('public'),
// },
module: {
rules: [
{
// vue-loader config to load `.vue` files or single file components.
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// https://vue-loader.vuejs.org/guide/scoped-css.html#mixing-local-and-global-styles
css: [
'vue-style-loader', {
loader: 'css-loader',
}
],
ts: [
'ts-loader',
],
js: [
'babel-loader',
],
},
cacheBusting: true,
},
},
{
test: /\.tsx?$/,
use: [
{loader: 'babel-loader'}, {
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.ts\.vue$/],
appendTsxSuffixTo: [/\.tsx\.vue$/]
}
}
],
exclude: /node_modules/
},
{
// This is required for other javascript you are gonna write besides
// vue.
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'),
resolve('node_modules/webpack-dev-server/client'),
],
},
]
},
plugins: [
new webpack.NamedModulesPlugin(),
new VueLoaderPlugin(),
// Exchanges, adds, or removes modules while an application is running,
// without a full reload.
new webpack.HotModuleReplacementPlugin(),
],
resolve: {
/**
* The compiler-included build of vue which allows to use vue templates
* without pre-compiling them
*/
alias: {
'vue$': 'vue/dist/vue.esm.js',
},
extensions: ['*', '.vue', '.js', '.ts', '.json'],
},
output: {
filename: 'scripts/applications/[name]/[name].bundle.js',
path: path.resolve(__dirname, 'public')
},
watchOptions: {aggregateTimeout: 300, poll: 1000},
// webpack outputs performance related stuff in the browser.
performance: {
hints: false,
},
node: {fs: 'empty'}
}