V2
This commit is contained in:
commit
df8123698f
51 changed files with 13058 additions and 37 deletions
5
.babelrc
Normal file
5
.babelrc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"presets": [["es2015", {"modules": false}], "stage-2"],
|
||||
"plugins": ["transform-runtime"],
|
||||
"comments": false
|
||||
}
|
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
3
.eslintignore
Normal file
3
.eslintignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
build/*.js
|
||||
config/*.js
|
||||
dist/*.js
|
31
.eslintrc.js
Normal file
31
.eslintrc.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true
|
||||
},
|
||||
globals: {
|
||||
'cordova': true,
|
||||
'DEV': true,
|
||||
'PROD': true,
|
||||
'__THEME': true
|
||||
},
|
||||
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
||||
extends: 'standard',
|
||||
// required to lint *.vue files
|
||||
plugins: [
|
||||
'html'
|
||||
],
|
||||
// add your custom rules here
|
||||
'rules': {
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 0,
|
||||
'one-var': 0,
|
||||
'import/first': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
'brace-style': [2, 'stroustrup', { 'allowSingleLine': true }]
|
||||
}
|
||||
}
|
49
.gitignore
vendored
49
.gitignore
vendored
|
@ -1,37 +1,12 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log
|
||||
npm-debug.log.*
|
||||
selenium-debug.log
|
||||
test/unit/coverage
|
||||
test/e2e/reports
|
||||
cordova/platforms
|
||||
cordova/plugins
|
||||
thumbs.db
|
||||
!.gitkeep
|
||||
|
|
35
.stylintrc
Normal file
35
.stylintrc
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"blocks": "never",
|
||||
"brackets": "never",
|
||||
"colons": "never",
|
||||
"colors": "always",
|
||||
"commaSpace": "always",
|
||||
"commentSpace": "always",
|
||||
"cssLiteral": "never",
|
||||
"depthLimit": false,
|
||||
"duplicates": true,
|
||||
"efficient": "always",
|
||||
"extendPref": false,
|
||||
"globalDupe": true,
|
||||
"indentPref": 2,
|
||||
"leadingZero": "never",
|
||||
"maxErrors": false,
|
||||
"maxWarnings": false,
|
||||
"mixed": false,
|
||||
"namingConvention": false,
|
||||
"namingConventionStrict": false,
|
||||
"none": "never",
|
||||
"noImportant": false,
|
||||
"parenSpace": "never",
|
||||
"placeholder": false,
|
||||
"prefixVarsWithDollar": "always",
|
||||
"quotePref": "single",
|
||||
"semicolons": "never",
|
||||
"sortOrder": false,
|
||||
"stackedProperties": "never",
|
||||
"trailingWhitespace": "never",
|
||||
"universal": "never",
|
||||
"valid": true,
|
||||
"zeroUnits": "never",
|
||||
"zIndexNormalize": false
|
||||
}
|
31
README.md
Normal file
31
README.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# My Spells
|
||||
|
||||
My Spells is an open source web-based application to elegantly view spells and save them to your local spellbook.
|
||||
|
||||
## License
|
||||
|
||||
Open Game License v1.0a Copyright 2000, Wizards of the Coast, Inc.
|
||||
|
||||
App contains content from the SRD and is restricted and covered by the OGL. You can find the OGL 1.0a at [ogl.html](./ogl.html) in this app's repo, or [online here](http://www.opengamingfoundation.org/ogl.html). When using said data, please make sure to conform appropriately with the proper licenses and whatnot.
|
||||
|
||||
## Credit
|
||||
|
||||
* ephe's [grimoire](https://github.com/ephe/grimoire/) spell list converted json by vorpalhex and cleaned up to meet OGL license standards [labeled under srd_spells](https://github.com/vorpalhex/srd_spells)
|
||||
* Built on [Vue.js](https://vuejs.org/) and the [Quasar Framework](http://quasar-framework.org/)
|
||||
* Logo magic wand icon by David from the Noun Project
|
||||
|
||||
## Build Setup
|
||||
|
||||
``` bash
|
||||
# install dependencies
|
||||
$ npm install
|
||||
|
||||
# serve with hot reload at localhost:8080
|
||||
$ npm run dev
|
||||
|
||||
# build for production with minification
|
||||
$ npm run build
|
||||
|
||||
# lint code
|
||||
$ npm run lint
|
||||
```
|
91
build/css-utils.js
Normal file
91
build/css-utils.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
var
|
||||
ExtractTextPlugin = require('extract-text-webpack-plugin'),
|
||||
autoprefixer = require('autoprefixer'),
|
||||
purify = require('purify-css'),
|
||||
glob = require('glob'),
|
||||
path = require('path'),
|
||||
fs = require('fs')
|
||||
|
||||
module.exports.postcss = [autoprefixer()]
|
||||
|
||||
module.exports.styleLoaders = function (options) {
|
||||
options = options || {}
|
||||
|
||||
function generateLoaders (loaders) {
|
||||
if (options.postcss) {
|
||||
loaders.splice(1, 0, 'postcss')
|
||||
}
|
||||
|
||||
var sourceLoader = loaders.map(function (loader) {
|
||||
var extraParamChar
|
||||
if (/\?/.test(loader)) {
|
||||
loader = loader.replace(/\?/, '-loader?')
|
||||
extraParamChar = '&'
|
||||
}
|
||||
else {
|
||||
loader = loader + '-loader'
|
||||
extraParamChar = '?'
|
||||
}
|
||||
return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
|
||||
}).join('!')
|
||||
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: sourceLoader,
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
}
|
||||
else {
|
||||
return ['vue-style-loader', sourceLoader].join('!')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
css: generateLoaders(['css']),
|
||||
less: generateLoaders(['css', 'less']),
|
||||
sass: generateLoaders(['css', 'sass?indentedSyntax']),
|
||||
scss: generateLoaders(['css', 'sass']),
|
||||
styl: generateLoaders(['css', 'stylus']),
|
||||
stylus: generateLoaders(['css', 'stylus'])
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.styleRules = function (options) {
|
||||
var output = []
|
||||
var loaders = exports.styleLoaders(options)
|
||||
for (var extension in loaders) {
|
||||
var loader = loaders[extension]
|
||||
output.push({
|
||||
test: new RegExp('\\.' + extension + '$'),
|
||||
loader: loader
|
||||
})
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function getSize (size) {
|
||||
return (size / 1024).toFixed(2) + 'kb'
|
||||
}
|
||||
|
||||
module.exports.purify = function(cb) {
|
||||
var css = glob.sync(path.join(__dirname, '../dist/**/*.css'))
|
||||
var js = glob.sync(path.join(__dirname, '../dist/**/*.js'))
|
||||
|
||||
Promise.all(css.map(function (file) {
|
||||
return new Promise(function (resolve) {
|
||||
console.log('\n Purifying ' + path.relative(path.join(__dirname, '../dist'), file).bold + '...')
|
||||
purify(js, [file], {minify: true}, function (purified) {
|
||||
var oldSize = fs.statSync(file).size
|
||||
fs.writeFileSync(file, purified)
|
||||
var newSize = fs.statSync(file).size
|
||||
|
||||
console.log(
|
||||
' * Reduced size by ' + ((1 - newSize / oldSize) * 100).toFixed(2) + '%, from ' +
|
||||
getSize(oldSize) + ' to ' + getSize(newSize) + '.'
|
||||
)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}))
|
||||
.then(cb)
|
||||
}
|
13
build/env-utils.js
Normal file
13
build/env-utils.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
var
|
||||
config = require('../config'),
|
||||
theme = process.argv[2] || config.defaultTheme
|
||||
|
||||
module.exports = {
|
||||
dev: process.env.NODE_ENV === 'development',
|
||||
prod: process.env.NODE_ENV === 'production',
|
||||
|
||||
platform: {
|
||||
theme: theme,
|
||||
cordovaAssets: './cordova/platforms/' + (theme === 'mat' ? 'android' : 'ios') + '/platform_www'
|
||||
}
|
||||
}
|
9
build/hot-reload.js
Normal file
9
build/hot-reload.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* eslint-disable */
|
||||
require('eventsource-polyfill')
|
||||
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
|
||||
|
||||
hotClient.subscribe(function (event) {
|
||||
if (event.action === 'reload') {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
50
build/script.build.js
Normal file
50
build/script.build.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
process.env.NODE_ENV = 'production'
|
||||
|
||||
require('colors')
|
||||
|
||||
var
|
||||
shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
env = require('./env-utils'),
|
||||
css = require('./css-utils'),
|
||||
config = require('../config'),
|
||||
webpack = require('webpack'),
|
||||
webpackConfig = require('./webpack.prod.conf'),
|
||||
targetPath = path.join(__dirname, '../dist')
|
||||
|
||||
console.log(' WARNING!'.bold)
|
||||
console.log(' Do NOT use VueRouter\'s "history" mode if')
|
||||
console.log(' building for Cordova or Electron.\n')
|
||||
|
||||
require('./script.clean.js')
|
||||
console.log((' Building Quasar App with "' + env.platform.theme + '" theme...\n').bold)
|
||||
|
||||
shell.mkdir('-p', targetPath)
|
||||
shell.cp('-R', 'src/statics', targetPath)
|
||||
|
||||
function finalize () {
|
||||
console.log((
|
||||
'\n Build complete with "' + env.platform.theme.bold + '" theme in ' +
|
||||
'"/dist"'.bold + ' folder.\n').cyan)
|
||||
|
||||
console.log(' Built files are meant to be served over an HTTP server.'.bold)
|
||||
console.log(' Opening index.html over file:// won\'t work.'.bold)
|
||||
}
|
||||
|
||||
webpack(webpackConfig, function (err, stats) {
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n')
|
||||
|
||||
if (config.build.purifyCSS) {
|
||||
css.purify(finalize)
|
||||
}
|
||||
else {
|
||||
finalize()
|
||||
}
|
||||
})
|
6
build/script.clean.js
Normal file
6
build/script.clean.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
var
|
||||
shell = require('shelljs'),
|
||||
path = require('path')
|
||||
|
||||
shell.rm('-rf', path.resolve(__dirname, '../dist'))
|
||||
console.log(' Cleaned build artifacts.\n')
|
86
build/script.dev.js
Normal file
86
build/script.dev.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
process.env.NODE_ENV = 'development'
|
||||
|
||||
require('colors')
|
||||
|
||||
var
|
||||
path = require('path'),
|
||||
express = require('express'),
|
||||
webpack = require('webpack'),
|
||||
env = require('./env-utils'),
|
||||
config = require('../config'),
|
||||
opn = require('opn'),
|
||||
proxyMiddleware = require('http-proxy-middleware'),
|
||||
webpackConfig = require('./webpack.dev.conf'),
|
||||
app = express(),
|
||||
port = process.env.PORT || config.dev.port,
|
||||
uri = 'http://localhost:' + port
|
||||
|
||||
console.log(' Starting dev server with "' + (process.argv[2] || env.platform.theme).bold + '" theme...')
|
||||
console.log(' Will listen at ' + uri.bold)
|
||||
if (config.dev.openBrowser) {
|
||||
console.log(' Browser will open when build is ready.\n')
|
||||
}
|
||||
|
||||
var compiler = webpack(webpackConfig)
|
||||
|
||||
// Define HTTP proxies to your custom API backend
|
||||
// https://github.com/chimurai/http-proxy-middleware
|
||||
var proxyTable = config.dev.proxyTable
|
||||
|
||||
var devMiddleware = require('webpack-dev-middleware')(compiler, {
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
quiet: true
|
||||
})
|
||||
|
||||
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
|
||||
log: function () {}
|
||||
})
|
||||
|
||||
// force page reload when html-webpack-plugin template changes
|
||||
compiler.plugin('compilation', function (compilation) {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
// proxy requests like API. See /config/index.js -> dev.proxyTable
|
||||
// https://github.com/chimurai/http-proxy-middleware
|
||||
Object.keys(proxyTable).forEach(function (context) {
|
||||
var options = proxyTable[context]
|
||||
if (typeof options === 'string') {
|
||||
options = { target: options }
|
||||
}
|
||||
app.use(proxyMiddleware(context, options))
|
||||
})
|
||||
|
||||
// handle fallback for HTML5 history API
|
||||
app.use(require('connect-history-api-fallback')())
|
||||
|
||||
// serve webpack bundle output
|
||||
app.use(devMiddleware)
|
||||
|
||||
// enable hot-reload and state-preserving
|
||||
// compilation error display
|
||||
app.use(hotMiddleware)
|
||||
|
||||
// serve pure static assets
|
||||
var staticsPath = path.posix.join(webpackConfig.output.publicPath, 'statics/')
|
||||
app.use(staticsPath, express.static('./src/statics'))
|
||||
|
||||
// try to serve Cordova statics for Play App
|
||||
app.use(express.static(env.platform.cordovaAssets))
|
||||
|
||||
module.exports = app.listen(port, function (err) {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
return
|
||||
}
|
||||
|
||||
// open browser if set so in /config/index.js
|
||||
if (config.dev.openBrowser) {
|
||||
devMiddleware.waitUntilValid(function () {
|
||||
opn(uri)
|
||||
})
|
||||
}
|
||||
})
|
115
build/webpack.base.conf.js
Normal file
115
build/webpack.base.conf.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
var
|
||||
path = require('path'),
|
||||
webpack = require('webpack'),
|
||||
config = require('../config'),
|
||||
cssUtils = require('./css-utils'),
|
||||
env = require('./env-utils'),
|
||||
merge = require('webpack-merge'),
|
||||
projectRoot = path.resolve(__dirname, '../'),
|
||||
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
|
||||
useCssSourceMap =
|
||||
(env.dev && config.dev.cssSourceMap) ||
|
||||
(env.prod && config.build.productionSourceMap)
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: './src/main.js'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
publicPath: config[env.prod ? 'build' : 'dev'].publicPath,
|
||||
filename: 'js/[name].js',
|
||||
chunkFilename: 'js/[id].[chunkhash].js'
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
modules: [
|
||||
resolve('src'),
|
||||
resolve('node_modules')
|
||||
],
|
||||
alias: config.aliases
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ // eslint
|
||||
enforce: 'pre',
|
||||
test: /\.(vue|js)$/,
|
||||
loader: 'eslint-loader',
|
||||
include: projectRoot,
|
||||
exclude: /node_modules/,
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: projectRoot,
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
postcss: cssUtils.postcss,
|
||||
loaders: merge({js: 'babel-loader'}, cssUtils.styleLoaders({
|
||||
sourceMap: useCssSourceMap,
|
||||
extract: env.prod
|
||||
}))
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'img/[name].[hash:7].[ext]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'fonts/[name].[hash:7].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
/*
|
||||
Take note!
|
||||
Uncomment if you wish to load only one Moment locale:
|
||||
|
||||
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
|
||||
*/
|
||||
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': config[env.prod ? 'build' : 'dev'].env,
|
||||
'DEV': env.dev,
|
||||
'PROD': env.prod,
|
||||
'__THEME': '"' + env.platform.theme + '"'
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: env.prod,
|
||||
options: {
|
||||
context: path.resolve(__dirname, '../src'),
|
||||
postcss: cssUtils.postcss
|
||||
}
|
||||
}),
|
||||
new ProgressBarPlugin({
|
||||
format: config.progressFormat
|
||||
})
|
||||
],
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
}
|
43
build/webpack.dev.conf.js
Normal file
43
build/webpack.dev.conf.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
var
|
||||
config = require('../config'),
|
||||
webpack = require('webpack'),
|
||||
merge = require('webpack-merge'),
|
||||
cssUtils = require('./css-utils'),
|
||||
baseWebpackConfig = require('./webpack.base.conf'),
|
||||
HtmlWebpackPlugin = require('html-webpack-plugin'),
|
||||
FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
|
||||
// add hot-reload related code to entry chunks
|
||||
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
|
||||
baseWebpackConfig.entry[name] = ['./build/hot-reload'].concat(baseWebpackConfig.entry[name])
|
||||
})
|
||||
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
// eval-source-map is faster for development
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
noInfo: true
|
||||
},
|
||||
module: {
|
||||
rules: cssUtils.styleRules({
|
||||
sourceMap: config.dev.cssSourceMap,
|
||||
postcss: true
|
||||
})
|
||||
},
|
||||
plugins: [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'src/index.html',
|
||||
inject: true
|
||||
}),
|
||||
new FriendlyErrorsPlugin({
|
||||
clearConsole: config.dev.clearConsoleOnRebuild
|
||||
})
|
||||
],
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
})
|
75
build/webpack.prod.conf.js
Normal file
75
build/webpack.prod.conf.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
var
|
||||
path = require('path'),
|
||||
config = require('../config'),
|
||||
cssUtils = require('./css-utils'),
|
||||
webpack = require('webpack'),
|
||||
merge = require('webpack-merge'),
|
||||
baseWebpackConfig = require('./webpack.base.conf'),
|
||||
ExtractTextPlugin = require('extract-text-webpack-plugin'),
|
||||
HtmlWebpackPlugin = require('html-webpack-plugin'),
|
||||
OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: cssUtils.styleRules({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true,
|
||||
postcss: true
|
||||
})
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
minimize: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: {
|
||||
safe: true
|
||||
}
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: '[name].[contenthash].css'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.build.index,
|
||||
template: 'src/index.html',
|
||||
inject: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks: function (module, count) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
chunks: ['vendor']
|
||||
})
|
||||
]
|
||||
})
|
6
config/dev.env.js
Normal file
6
config/dev.env.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
var merge = require('webpack-merge')
|
||||
var prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"'
|
||||
})
|
64
config/index.js
Normal file
64
config/index.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
var path = require('path')
|
||||
|
||||
module.exports = {
|
||||
// Webpack aliases
|
||||
aliases: {
|
||||
quasar: path.resolve(__dirname, '../node_modules/quasar-framework/'),
|
||||
src: path.resolve(__dirname, '../src'),
|
||||
assets: path.resolve(__dirname, '../src/assets'),
|
||||
components: path.resolve(__dirname, '../src/components')
|
||||
},
|
||||
|
||||
// Progress Bar Webpack plugin format
|
||||
// https://github.com/clessg/progress-bar-webpack-plugin#options
|
||||
progressFormat: ' [:bar] ' + ':percent'.bold + ' (:msg)',
|
||||
|
||||
// Default theme to build with ('ios' or 'mat')
|
||||
defaultTheme: 'mat',
|
||||
|
||||
build: {
|
||||
env: require('./prod.env'),
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
publicPath: '',
|
||||
productionSourceMap: false,
|
||||
|
||||
// Remove unused CSS
|
||||
// Disable it if it has side-effects for your specific app
|
||||
purifyCSS: true
|
||||
},
|
||||
dev: {
|
||||
env: require('./dev.env'),
|
||||
cssSourceMap: true,
|
||||
// auto open browser or not
|
||||
openBrowser: true,
|
||||
publicPath: '/',
|
||||
port: 8080,
|
||||
|
||||
// If for example you are using Quasar Play
|
||||
// to generate a QR code then on each dev (re)compilation
|
||||
// you need to avoid clearing out the console, so set this
|
||||
// to "false", otherwise you can set it to "true" to always
|
||||
// have only the messages regarding your last (re)compilation.
|
||||
clearConsoleOnRebuild: false,
|
||||
|
||||
// Proxy your API if using any.
|
||||
// Also see /build/script.dev.js and search for "proxy api requests"
|
||||
// https://github.com/chimurai/http-proxy-middleware
|
||||
proxyTable: {}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* proxyTable example:
|
||||
*
|
||||
proxyTable: {
|
||||
// proxy all requests starting with /api
|
||||
'/api': {
|
||||
target: 'https://some.address.com/api',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
3
config/prod.env.js
Normal file
3
config/prod.env.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
version: '2'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
image: your-docker-org/your-app-name
|
||||
command: sh
|
||||
volumes:
|
||||
- .:/opt/app
|
||||
- /opt/app/node_modules
|
||||
ports:
|
||||
- 8080:8080
|
||||
tty: true
|
15
dockerfile
Normal file
15
dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
|||
#change this to your own repo, should you have uploaded your image!
|
||||
FROM quasarframework/client-dev:latest
|
||||
|
||||
MAINTAINER Your Name <your.email@your-sites-address.com>
|
||||
|
||||
WORKDIR /opt/app
|
||||
|
||||
COPY package.json /opt/app/
|
||||
RUN npm install
|
||||
|
||||
COPY . /opt/app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD /bin/sh
|
133
ogl.html
Normal file
133
ogl.html
Normal file
|
@ -0,0 +1,133 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Open Game License v0.1 Simplified</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<center><a href="./index.html">OGF Main</a> |
|
||||
<a href="./licenses.html">List of Licenses</a> |
|
||||
Open Game License Text</center>
|
||||
|
||||
<P>THIS LICENSE IS APPROVED FOR GENERAL USE. PERMISSION TO DISTRIBUTE THIS LICENSE
|
||||
IS MADE BY WIZARDS OF THE COAST!</P>
|
||||
|
||||
<P>OPEN GAME LICENSE Version 1.0a</P>
|
||||
|
||||
<P>The following text is the property of Wizards of the Coast, Inc. and is
|
||||
Copyright 2000 Wizards of the Coast, Inc ("Wizards"). All Rights Reserved.</P>
|
||||
|
||||
<P>1. Definitions: (a)"Contributors" means the copyright and/or trademark
|
||||
owners who have contributed Open Game Content; (b)"Derivative Material"
|
||||
means copyrighted material including derivative works and translations
|
||||
(including into other computer languages), potation, modification,
|
||||
correction, addition, extension, upgrade, improvement, compilation,
|
||||
abridgment or other form in which an existing work may be recast,
|
||||
transformed or adapted; (c) "Distribute" means to reproduce, license, rent,
|
||||
lease, sell, broadcast, publicly display, transmit or otherwise distribute;
|
||||
(d)"Open Game Content" means the game mechanic and includes the methods,
|
||||
procedures, processes and routines to the extent such content does not
|
||||
embody the Product Identity and is an enhancement over the prior art and any
|
||||
additional content clearly identified as Open Game Content by the
|
||||
Contributor, and means any work covered by this License, including
|
||||
translations and derivative works under copyright law, but specifically
|
||||
excludes Product Identity. (e) "Product Identity" means product and product
|
||||
line names, logos and identifying marks including trade dress; artifacts;
|
||||
creatures characters; stories, storylines, plots, thematic elements,
|
||||
dialogue, incidents, language, artwork, symbols, designs, depictions,
|
||||
likenesses, formats, poses, concepts, themes and graphic, photographic and
|
||||
other visual or audio representations; names and descriptions of characters,
|
||||
spells, enchantments, personalities, teams, personas, likenesses and special
|
||||
abilities; places, locations, environments, creatures, equipment, magical or
|
||||
supernatural abilities or effects, logos, symbols, or graphic designs; and
|
||||
any other trademark or registered trademark clearly identified as Product
|
||||
identity by the owner of the Product Identity, and which specifically
|
||||
excludes the Open Game Content; (f) "Trademark" means the logos, names,
|
||||
mark, sign, motto, designs that are used by a Contributor to identify itself
|
||||
or its products or the associated products contributed to the Open Game
|
||||
License by the Contributor (g) "Use", "Used" or "Using" means to use,
|
||||
Distribute, copy, edit, format, modify, translate and otherwise create
|
||||
Derivative Material of Open Game Content. (h) "You" or "Your" means the
|
||||
licensee in terms of this agreement.</P>
|
||||
|
||||
<P>2. The License: This License applies to any Open Game Content that contains
|
||||
a notice indicating that the Open Game Content may only be Used under and in
|
||||
terms of this License. You must affix such a notice to any Open Game Content
|
||||
that you Use. No terms may be added to or subtracted from this License
|
||||
except as described by the License itself. No other terms or conditions may
|
||||
be applied to any Open Game Content distributed using this License.</P>
|
||||
|
||||
<P>3.Offer and Acceptance: By Using the Open Game Content You indicate Your
|
||||
acceptance of the terms of this License.</P>
|
||||
|
||||
<P>4. Grant and Consideration: In consideration for agreeing to use this
|
||||
License, the Contributors grant You a perpetual, worldwide, royalty-free,
|
||||
non-exclusive license with the exact terms of this License to Use, the Open
|
||||
Game Content.</P>
|
||||
|
||||
<P>5.Representation of Authority to Contribute: If You are contributing
|
||||
original material as Open Game Content, You represent that Your
|
||||
Contributions are Your original creation and/or You have sufficient rights
|
||||
to grant the rights conveyed by this License.</P>
|
||||
|
||||
<P>6.Notice of License Copyright: You must update the COPYRIGHT NOTICE portion
|
||||
of this License to include the exact text of the COPYRIGHT NOTICE of any
|
||||
Open Game Content You are copying, modifying or distributing, and You must
|
||||
add the title, the copyright date, and the copyright holder's name to the
|
||||
COPYRIGHT NOTICE of any original Open Game Content you Distribute.</P>
|
||||
|
||||
<P>7. Use of Product Identity: You agree not to Use any Product Identity,
|
||||
including as an indication as to compatibility, except as expressly licensed
|
||||
in another, independent Agreement with the owner of each element of that
|
||||
Product Identity. You agree not to indicate compatibility or
|
||||
co-adaptability with any Trademark or Registered Trademark in conjunction with a work containing
|
||||
Open Game Content except as expressly licensed in another, independent
|
||||
Agreement with the owner of such Trademark or Registered Trademark. The use of any Product Identity
|
||||
in Open Game Content does not constitute a challenge to the ownership of
|
||||
that Product Identity. The owner of any Product Identity used in Open Game
|
||||
Content shall retain all rights, title and interest in and to that Product
|
||||
Identity.</P>
|
||||
|
||||
<P>8. Identification: If you distribute Open Game Content You must clearly
|
||||
indicate which portions of the work that you are distributing are Open Game
|
||||
Content.</P>
|
||||
|
||||
<P>9. Updating the License: Wizards or its designated Agents may publish
|
||||
updated versions of this License. You may use any authorized version of
|
||||
this License to copy, modify and distribute any Open Game Content originally
|
||||
distributed under any version of this License.</P>
|
||||
|
||||
<P>10 Copy of this License: You MUST include a copy of this License with every
|
||||
copy of the Open Game Content You Distribute.</P>
|
||||
|
||||
<P>11. Use of Contributor Credits: You may not market or advertise the Open
|
||||
Game Content using the name of any Contributor unless You have written
|
||||
permission from the Contributor to do so.</P>
|
||||
|
||||
<P>12 Inability to Comply: If it is impossible for You to comply with any of
|
||||
the terms of this License with respect to some or all of the Open Game
|
||||
Content due to statute, judicial order, or governmental regulation then You
|
||||
may not Use any Open Game Material so affected.</P>
|
||||
|
||||
<P>13 Termination: This License will terminate automatically if You fail to
|
||||
comply with all terms herein and fail to cure such breach within 30 days of
|
||||
becoming aware of the breach. All sublicenses shall survive the termination
|
||||
of this License.</P>
|
||||
|
||||
<P>14 Reformation: If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent necessary
|
||||
to make it enforceable.</P>
|
||||
|
||||
<P>15 COPYRIGHT NOTICE<br>
|
||||
Open Game License v 1.0 Copyright 2000, Wizards of the Coast, Inc.</P>
|
||||
|
||||
<p>
|
||||
|
||||
<center><a href="./index.html">OGF Main</a></Center>
|
||||
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
70
package.json
Normal file
70
package.json
Normal file
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"name": "My Spells",
|
||||
"version": "2.0.0",
|
||||
"description": "My Spells is an open source web-based application to elegantly view spells and save them to your local spellbook.",
|
||||
"author": "Joe Wroten <joe@wroten.me>",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"clean": "node build/script.clean.js",
|
||||
"dev": "node build/script.dev.js",
|
||||
"build": "node build/script.build.js",
|
||||
"lint": "eslint --ext .js,.vue src"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.0.0",
|
||||
"fastclick": "^1.0.6",
|
||||
"marked": "^0.3.6",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"moment": "^2.15.0",
|
||||
"quasar-framework": "^0.13.4",
|
||||
"roboto-fontface": "^0.7.0",
|
||||
"vue": "^2.3.0",
|
||||
"vue-router": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.4.0",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^7.0.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-transform-runtime": "^6.0.0",
|
||||
"babel-preset-es2015": "^6.0.0",
|
||||
"babel-preset-stage-2": "^6.0.0",
|
||||
"colors": "^1.1.2",
|
||||
"connect-history-api-fallback": "^1.1.0",
|
||||
"css-loader": "^0.28.0",
|
||||
"eslint": "^3.0.1",
|
||||
"eslint-config-standard": "^10.2.1",
|
||||
"eslint-friendly-formatter": "^2.0.5",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"eslint-plugin-html": "^2.0.1",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-node": "^4.2.2",
|
||||
"eslint-plugin-promise": "^3.3.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"express": "^4.13.3",
|
||||
"extract-text-webpack-plugin": "^2.0.0-beta.4",
|
||||
"file-loader": "^0.11.1",
|
||||
"friendly-errors-webpack-plugin": "^1.1.3",
|
||||
"html-webpack-plugin": "^2.8.1",
|
||||
"http-proxy-middleware": "^0.17.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"opn": "^5.0.0",
|
||||
"optimize-css-assets-webpack-plugin": "^1.3.1",
|
||||
"postcss-loader": "^1.0.0",
|
||||
"progress-bar-webpack-plugin": "^1.9.0",
|
||||
"purify-css": "^1.1.9",
|
||||
"shelljs": "^0.7.0",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus-loader": "^3.0.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"vue-loader": "^12.0.2",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.3.0",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-middleware": "^1.8.4",
|
||||
"webpack-hot-middleware": "^2.17.0",
|
||||
"webpack-merge": "^4.0.0",
|
||||
"whatwg-fetch": "^2.0.3"
|
||||
}
|
||||
}
|
84
src/App.vue
Normal file
84
src/App.vue
Normal file
|
@ -0,0 +1,84 @@
|
|||
<template>
|
||||
<div id="q-app">
|
||||
<q-layout class="bg-light">
|
||||
<nav-header
|
||||
slot="header"
|
||||
class="toolbar dark"
|
||||
></nav-header>
|
||||
|
||||
<div class="layout-view">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
|
||||
<nav-footer
|
||||
slot="footer"
|
||||
class="toolbar pink"
|
||||
></nav-footer>
|
||||
</q-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LocalStorage, Loading, Dialog } from 'quasar'
|
||||
import Vue from 'vue'
|
||||
import 'whatwg-fetch'
|
||||
import { state, dispatch } from './store'
|
||||
import Header from './components/Header'
|
||||
import Footer from './components/Footer'
|
||||
|
||||
Vue.component('nav-header', Header)
|
||||
Vue.component('nav-footer', Footer)
|
||||
|
||||
function fetchSuccess (data) {
|
||||
dispatch({
|
||||
type: 'SPELLS_RESOLVED',
|
||||
data: {
|
||||
data,
|
||||
loaded: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function fetchFailure (reason) {
|
||||
let message = 'Unable to retrieve spells list'
|
||||
Dialog.create({
|
||||
title: 'Error',
|
||||
message,
|
||||
nobuttons: true
|
||||
})
|
||||
console.error(message, reason)
|
||||
}
|
||||
|
||||
function fetchSpells () {
|
||||
fetch('./statics/dnd5e.json')
|
||||
.then(response => response.json())
|
||||
.then(fetchSuccess)
|
||||
.catch(fetchFailure)
|
||||
.then(() => { Loading.hide() })
|
||||
}
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
},
|
||||
mounted () {
|
||||
if (LocalStorage.has('chosen')) {
|
||||
dispatch({
|
||||
type: 'LOAD_LOCAL_CHOSEN'
|
||||
})
|
||||
}
|
||||
|
||||
if (!this.state.spells.loaded) {
|
||||
Loading.show()
|
||||
|
||||
fetchSpells()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="stylus">
|
||||
.toolbar > .q-picker-textfield
|
||||
margin: 0 .75em
|
||||
</style>
|
72
src/components/About.vue
Normal file
72
src/components/About.vue
Normal file
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<main class="content">
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
My Spells is an <a href="https://github.com/sharpshark28/my_spells"><i>code</i> open source</a>
|
||||
web-based application to elegantly view spells and save them to your local spellbook.
|
||||
</p>
|
||||
|
||||
<h2>Settings</h2>
|
||||
<button
|
||||
class="primary"
|
||||
v-on:click="wipeChosen"
|
||||
:disabled="disableChosen"
|
||||
>
|
||||
<i class="on-left">delete</i>
|
||||
Reset Spellbook
|
||||
</button>
|
||||
|
||||
<h2>License</h2>
|
||||
<p>
|
||||
Open Game License v1.0a Copyright 2000, Wizards of the Coast, Inc.
|
||||
</p>
|
||||
<p>
|
||||
App contains content from the SRD and is restricted and covered by the OGL. You can find the OGL 1.0a at ogl.html in this app's repo, or <a href="http://www.opengamingfoundation.org/ogl.html">online here</a>. When using said data, please make sure to conform appropriately with the proper licenses and whatnot.
|
||||
</p>
|
||||
|
||||
<h2>Credit</h2>
|
||||
<ul>
|
||||
<li>
|
||||
ephe's <a href="https://github.com/ephe/grimoire/">grimoire</a> spell list converted json by vorpalhex and cleaned up to meet OGL license standards <a href="https://github.com/vorpalhex/srd_spells">labeled under srd_spells</a>
|
||||
<li>
|
||||
Built on <a href="https://vuejs.org/">Vue.js</a>
|
||||
and the <a href="http://quasar-framework.org/">Quasar Framework</a>
|
||||
</li>
|
||||
<li>
|
||||
Logo magic wand icon by David from the Noun Project
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Toast } from 'quasar'
|
||||
import { dispatch, state } from '../store'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
},
|
||||
computed: {
|
||||
disableChosen () {
|
||||
return this.state.chosen.length === 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
wipeChosen () {
|
||||
dispatch({
|
||||
type: 'WIPE_CHOSEN'
|
||||
})
|
||||
Toast.create({
|
||||
html: 'Spellbook Reset'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
main
|
||||
padding: 0 2em
|
||||
max-width: 40rem
|
||||
</style>
|
31
src/components/Empty.vue
Normal file
31
src/components/Empty.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<div class="empty text-center">
|
||||
<h1>
|
||||
{{title}}
|
||||
</h1>
|
||||
<p>
|
||||
<slot>
|
||||
This page is empty.
|
||||
</slot>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
props: [
|
||||
'title'
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.empty
|
||||
height: 100%
|
||||
background-image: url('/statics/empty-bg.svg')
|
||||
background-repeat: no-repeat
|
||||
background-position: top center
|
||||
</style>
|
58
src/components/Error404.vue
Normal file
58
src/components/Error404.vue
Normal file
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div class="error-page window-height window-width bg-light column items-center">
|
||||
<div class="error-code bg-primary flex items-center justify-center">
|
||||
404
|
||||
</div>
|
||||
<div>
|
||||
<div class="error-card card bg-white column items-center justify-center">
|
||||
<i class="text-grey-5">error_outline</i>
|
||||
<p class="caption text-center">Oops. Nothing here...</p>
|
||||
<p class="text-center group">
|
||||
<button v-if="canGoBack" class="grey push small" @click="goBack">
|
||||
<i class="on-left">keyboard_arrow_left</i>
|
||||
Go back
|
||||
</button>
|
||||
<router-link to="/">
|
||||
<button class="grey push small">
|
||||
Go home
|
||||
<i class="on-right">home</i>
|
||||
</button>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
canGoBack: window.history.length > 1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack () {
|
||||
window.history.go(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.error-page
|
||||
.error-code
|
||||
height 50vh
|
||||
width 100%
|
||||
padding-top 15vh
|
||||
font-size 30vmax
|
||||
color rgba(255, 255, 255, .2)
|
||||
overflow hidden
|
||||
.error-card
|
||||
margin-top -25px
|
||||
width 90vw
|
||||
max-width 600px
|
||||
padding 50px
|
||||
i
|
||||
font-size 5rem
|
||||
</style>
|
67
src/components/Filter.vue
Normal file
67
src/components/Filter.vue
Normal file
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<q-select
|
||||
type="list"
|
||||
v-model="state.sortBy"
|
||||
:options="sortByOptions"
|
||||
></q-select>
|
||||
|
||||
<q-search
|
||||
v-model="state.search"
|
||||
@input="searchChanged"
|
||||
></q-search>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { state, dispatch } from '../store'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
},
|
||||
computed: {
|
||||
sortByOptions () {
|
||||
let options = [
|
||||
{
|
||||
label: 'Name',
|
||||
value: 'name'
|
||||
},
|
||||
{
|
||||
label: 'Level',
|
||||
value: 'level'
|
||||
}
|
||||
]
|
||||
|
||||
if (this.state.search.length >= 3) {
|
||||
options.unshift({
|
||||
label: 'Relevance',
|
||||
value: 'sortScore'
|
||||
})
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchChanged (newSearch) {
|
||||
if (newSearch.length >= 3) {
|
||||
this.state.previousSortBy = this.state.sortBy
|
||||
this.state.sortBy = 'sortScore'
|
||||
}
|
||||
else {
|
||||
this.state.sortBy = this.state.previousSortBy
|
||||
}
|
||||
|
||||
dispatch({ type: 'SEARCH_CHANGED' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.row
|
||||
padding: 1em
|
||||
.q-picker-textfield
|
||||
margin-right: 1em
|
||||
</style>
|
55
src/components/Footer.vue
Normal file
55
src/components/Footer.vue
Normal file
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<footer>
|
||||
<q-tabs
|
||||
class="pink justified"
|
||||
:refs="$refs"
|
||||
default-tab="tab-all"
|
||||
>
|
||||
<q-tab
|
||||
icon="local_library"
|
||||
route="/"
|
||||
exact
|
||||
>
|
||||
All Spells
|
||||
</q-tab>
|
||||
<q-tab
|
||||
icon="bookmark"
|
||||
route="/my"
|
||||
exact
|
||||
>
|
||||
My Spells
|
||||
<span
|
||||
class="label pointing-left bg-yellow text-dark"
|
||||
v-show="state.chosen.length"
|
||||
>{{state.chosen.length}}</span>
|
||||
</q-tab>
|
||||
<q-tab
|
||||
hidden="true"
|
||||
route="/spell"
|
||||
>
|
||||
Spell
|
||||
</q-tab>
|
||||
<q-tab
|
||||
hidden="true"
|
||||
route="/about"
|
||||
>
|
||||
About
|
||||
</q-tab>
|
||||
</q-tabs>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { state } from '../store'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
footer .q-tabs
|
||||
width: 100%
|
||||
</style>
|
45
src/components/Header.vue
Normal file
45
src/components/Header.vue
Normal file
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-toolbar-title :padding="0">
|
||||
<router-link
|
||||
to="/my"
|
||||
class="text-white"
|
||||
>
|
||||
<img class="logo" src="statics/logo.svg" alt="" />
|
||||
My Spells
|
||||
<small>5e Personal Spellbook</small>
|
||||
</router-link>
|
||||
</q-toolbar-title>
|
||||
|
||||
<router-link
|
||||
to="/about"
|
||||
tag="button"
|
||||
>
|
||||
<i>help</i>
|
||||
<q-tooltip
|
||||
anchor="bottom right"
|
||||
self="top right"
|
||||
>
|
||||
About
|
||||
</q-tooltip>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { state } from '../store'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.logo
|
||||
height: 1.25em
|
||||
vertical-align: middle
|
||||
</style>
|
35
src/components/Index.vue
Normal file
35
src/components/Index.vue
Normal file
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<main>
|
||||
<spell-list
|
||||
v-if="state.spells.data.length"
|
||||
:spells="state.spells.data"
|
||||
></spell-list>
|
||||
<page-empty
|
||||
v-else
|
||||
title="Writing Spellbook..."
|
||||
>
|
||||
Please wait...
|
||||
</page-empty>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Empty from './Empty'
|
||||
import SpellList from './Spelllist'
|
||||
import { state } from '../store'
|
||||
|
||||
Vue.component('page-empty', Empty)
|
||||
Vue.component('spell-list', SpellList)
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
main
|
||||
height: 90%
|
||||
</style>
|
49
src/components/Myspells.vue
Normal file
49
src/components/Myspells.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<main>
|
||||
<spell-list
|
||||
v-if="mySpells.length"
|
||||
:spells="mySpells"
|
||||
></spell-list>
|
||||
<page-empty
|
||||
v-else
|
||||
title="You haven't chosen any spells"
|
||||
>
|
||||
Try <i>bookmark</i> bookmarking spells from the
|
||||
<router-link to="/">all spells page</router-link> to add to your spellbook.
|
||||
</page-empty>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Empty from './Empty'
|
||||
import SpellList from './Spelllist'
|
||||
import { state } from '../store'
|
||||
|
||||
Vue.component('page-empty', Empty)
|
||||
Vue.component('spell-list', SpellList)
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
},
|
||||
computed: {
|
||||
mySpells () {
|
||||
if (!this.state.spells.loaded || this.state.chosen.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
return this.state.chosen.map(chosen => {
|
||||
return this.state.spells.data.find(spell => {
|
||||
return spell.name === chosen
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
main
|
||||
height: 90%
|
||||
</style>
|
180
src/components/Spell.vue
Normal file
180
src/components/Spell.vue
Normal file
|
@ -0,0 +1,180 @@
|
|||
<template>
|
||||
<div class="page-spell">
|
||||
<router-link
|
||||
tag="button"
|
||||
to="/"
|
||||
class="page-back-small primary shadow-1"
|
||||
>
|
||||
<i>arrow_back</i>
|
||||
Back
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
tag="button"
|
||||
to="/"
|
||||
class="page-back-big primary circular big shadow-2"
|
||||
>
|
||||
<i>arrow_back</i>
|
||||
</router-link>
|
||||
|
||||
<div class="card bg-white">
|
||||
<div class="card-title bg-pink text-white">
|
||||
{{spell.name}}
|
||||
<span class="label bg-white text-pink">
|
||||
Level {{level}}
|
||||
</span>
|
||||
|
||||
<q-checkbox
|
||||
class="float-right pink"
|
||||
v-model="checked"
|
||||
@input="toggle"
|
||||
></q-checkbox>
|
||||
<i
|
||||
class="bookmark float-right text-yellow pointer"
|
||||
v-if="checked"
|
||||
v-on:click="checked = false"
|
||||
>bookmark</i>
|
||||
<i
|
||||
class="bookmark float-right text-grey-5 pointer"
|
||||
v-else
|
||||
v-on:click="checked = true"
|
||||
>bookmark_border</i>
|
||||
</div>
|
||||
<ol class="list no-border">
|
||||
<li class="item">
|
||||
<i class="item-primary">short_text</i>
|
||||
<div
|
||||
class="item-content description"
|
||||
v-html="prettyDescription"
|
||||
></div>
|
||||
</li>
|
||||
<li class="item">
|
||||
<i class="item-primary">accessibility</i>
|
||||
<div class="item-content">
|
||||
{{classes}}
|
||||
</div>
|
||||
</li>
|
||||
<li class="item">
|
||||
<i class="item-primary">group_work</i>
|
||||
<div class="item-content">
|
||||
{{spell.components.raw}}
|
||||
</div>
|
||||
</li>
|
||||
<li class="item">
|
||||
<i class="item-primary">school</i>
|
||||
<div class="item-content">
|
||||
{{spell.school}}
|
||||
</div>
|
||||
</li>
|
||||
<li class="item">
|
||||
<i class="item-primary">hourglass_full</i>
|
||||
<div class="item-content">
|
||||
{{spell.duration}}
|
||||
</div>
|
||||
</li>
|
||||
<li class="item">
|
||||
<i class="item-primary">av_timer</i>
|
||||
<div class="item-content">
|
||||
{{spell.casting_time}}
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dispatch, state } from '../store'
|
||||
import { capitalize } from '../utils'
|
||||
import Marked from 'marked'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { state }
|
||||
},
|
||||
mounted () {
|
||||
state.lastSpell = this.state.spell.data.name
|
||||
},
|
||||
computed: {
|
||||
checked () {
|
||||
return this.state.chosen.indexOf(this.spell.name) >= 0
|
||||
},
|
||||
spell () {
|
||||
return this.state.spells.data.find(spell => spell.name === this.$route.params.name)
|
||||
},
|
||||
level () {
|
||||
return this.spell.level.toLowerCase() === 'cantrip' ? 'C' : this.spell.level
|
||||
},
|
||||
classes () {
|
||||
return this.spell.classes.map(cla => capitalize(cla)).join(', ')
|
||||
},
|
||||
prettyDescription () {
|
||||
let newDescription = this.spell.description
|
||||
let keywords = ['constitution', 'con', 'intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check']
|
||||
keywords.forEach(word => {
|
||||
let r = new RegExp(` ${word} `, 'gi')
|
||||
newDescription = newDescription.replace(r, o => ` _${o.trim()}_ `)
|
||||
})
|
||||
|
||||
newDescription = newDescription.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => ` **${o.trim()}** `)
|
||||
return Marked(newDescription)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle (want) {
|
||||
dispatch({
|
||||
type: 'CHANGE_CHOSEN',
|
||||
data: {
|
||||
want,
|
||||
name: this.spell.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.pointer
|
||||
cursor: pointer
|
||||
.page-spell
|
||||
width: 100%
|
||||
height: 100%
|
||||
position: relative
|
||||
padding: 1rem
|
||||
.page-back-small
|
||||
margin-bottom: 1rem
|
||||
.page-back-big
|
||||
display: none
|
||||
.card
|
||||
margin: 0 auto
|
||||
max-width: 40rem
|
||||
.description
|
||||
padding-bottom: 0
|
||||
.card-title .label
|
||||
margin-left: .5em
|
||||
.list
|
||||
padding-left: 16px
|
||||
.item
|
||||
height: auto
|
||||
.item-primary
|
||||
margin: 12px 0
|
||||
.item-content
|
||||
margin-left: 50px
|
||||
.bookmark
|
||||
font-size: 1.25em
|
||||
line-height: 1em
|
||||
@media screen and (min-height: 800px)
|
||||
.card
|
||||
position: relative
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
.page-back-big
|
||||
display: block
|
||||
position: absolute
|
||||
top: 1rem
|
||||
left: 1rem
|
||||
z-index: 1
|
||||
.page-back-small
|
||||
display: none
|
||||
</style>
|
79
src/components/Spellitem.vue
Normal file
79
src/components/Spellitem.vue
Normal file
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<div v-bind:class="{'checked': checked}">
|
||||
<div class="item-primary">
|
||||
{{level}}
|
||||
</div>
|
||||
<div class="item-content has-secondary" v-on:click="openSpell">
|
||||
<div>
|
||||
{{spell.name}}
|
||||
</div>
|
||||
<div>
|
||||
{{classes}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-secondary">
|
||||
<i
|
||||
class="float-left text-pink"
|
||||
v-if="checked"
|
||||
v-on:click="checked = false"
|
||||
>bookmark</i>
|
||||
<i
|
||||
class="float-left text-grey-5"
|
||||
v-else
|
||||
v-on:click="checked = true"
|
||||
>bookmark_border</i>
|
||||
<q-checkbox
|
||||
class="float-right pink"
|
||||
v-model="checked"
|
||||
@input="toggle"
|
||||
></q-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { state, dispatch } from '../store'
|
||||
import { capitalize } from '../utils'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
level () {
|
||||
return this.spell.level.toLowerCase() === 'cantrip' ? '0' : this.spell.level.charAt(0)
|
||||
},
|
||||
school () {
|
||||
return capitalize(this.spell.school)
|
||||
},
|
||||
classes () {
|
||||
return this.spell.classes.map(cla => capitalize(cla)).join(', ')
|
||||
},
|
||||
checked () {
|
||||
return this.state.chosen.indexOf(this.spell.name) >= 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return { state }
|
||||
},
|
||||
props: [
|
||||
'spell'
|
||||
],
|
||||
methods: {
|
||||
openSpell (event) {
|
||||
this.$router.push('/spell/' + this.spell.name)
|
||||
},
|
||||
toggle (want) {
|
||||
dispatch({
|
||||
type: 'CHANGE_CHOSEN',
|
||||
data: {
|
||||
want,
|
||||
name: this.spell.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.item-secondary
|
||||
width: 50px
|
||||
</style>
|
81
src/components/Spelllist.vue
Normal file
81
src/components/Spelllist.vue
Normal file
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<div class="spell-list-container">
|
||||
<nav-filter></nav-filter>
|
||||
<section class="spell-list list striped no-border" id="spell_list">
|
||||
<label
|
||||
is="spell-item"
|
||||
class="item two-lines item-link"
|
||||
v-for="spell in pagedSpells"
|
||||
:spell="spell"
|
||||
>
|
||||
</label>
|
||||
<q-pagination
|
||||
class="text-center"
|
||||
v-model="state.page"
|
||||
:max="numPages"
|
||||
></q-pagination>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import { Utils } from 'quasar'
|
||||
import Query from '../query'
|
||||
import Filter from './Filter'
|
||||
import SpellItem from './Spellitem'
|
||||
import { state } from '../store'
|
||||
|
||||
Vue.component('nav-filter', Filter)
|
||||
Vue.component('spell-item', SpellItem)
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
state,
|
||||
perPage: 0
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'spells'
|
||||
],
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
window.addEventListener('resize', this.newPerPage)
|
||||
})
|
||||
this.calculateNewPage()
|
||||
},
|
||||
methods: {
|
||||
newPerPage: Utils.debounce(function () {
|
||||
this.calculateNewPage()
|
||||
}, 100),
|
||||
calculateNewPage () {
|
||||
let fontSize = 14
|
||||
this.perPage = Math.floor(window.innerHeight / (fontSize * 5)) - 4
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
numPages () {
|
||||
return Math.ceil(this.filteredSpells.length / this.perPage)
|
||||
},
|
||||
filteredSpells () {
|
||||
return new Query(this.spells)
|
||||
.search('name', this.state.search)
|
||||
.sort(this.state.sortBy)
|
||||
.results
|
||||
},
|
||||
pagedSpells () {
|
||||
return new Query(this.filteredSpells)
|
||||
.paginate(this.state.page, this.perPage)
|
||||
.results
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.list.striped .item:nth-child(2n).checked
|
||||
background: #fff9c4
|
||||
.list.striped .item:nth-child(2n+1).checked
|
||||
background: #fffde7
|
||||
</style>
|
16
src/index.html
Normal file
16
src/index.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
|
||||
|
||||
<title>My Spells DnD5e</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="statics/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<div id="q-app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
22
src/main.js
Normal file
22
src/main.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
// === DEFAULT / CUSTOM STYLE ===
|
||||
// WARNING! always comment out ONE of the two require() calls below.
|
||||
// 1. use next line to activate CUSTOM STYLE (./src/themes)
|
||||
// require(`./themes/app.${__THEME}.styl`)
|
||||
// 2. or, use next line to activate DEFAULT QUASAR STYLE
|
||||
require(`quasar/dist/quasar.${__THEME}.css`)
|
||||
// ==============================
|
||||
|
||||
import Vue from 'vue'
|
||||
import Quasar from 'quasar'
|
||||
import router from './router'
|
||||
|
||||
Vue.use(Quasar) // Install Quasar Framework
|
||||
|
||||
Quasar.start(() => {
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#q-app',
|
||||
router,
|
||||
render: h => h(require('./App'))
|
||||
})
|
||||
})
|
40
src/query.js
Normal file
40
src/query.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
export default class Query {
|
||||
constructor (data) {
|
||||
this.data = data.map(item => {
|
||||
item.sortScore = 0
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
get results () {
|
||||
return this.data
|
||||
}
|
||||
|
||||
search (key, term = '', score = 0) {
|
||||
if (term.length >= 3) {
|
||||
this.data = this.data.filter(item => {
|
||||
let regFind = new RegExp(term, 'gi')
|
||||
let termMatches = (item[key].match(regFind) || []).length
|
||||
item.sortScore += termMatches
|
||||
return termMatches
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
sort (key = 'sortScore') {
|
||||
this.data = this.data.sort((a, b) => {
|
||||
if (a[key] < b[key]) return -1
|
||||
if (a[key] > b[key]) return 1
|
||||
return 0
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
paginate (page = 1, perPage = 10) {
|
||||
let min = page * perPage - perPage
|
||||
let max = min + perPage
|
||||
this.data = this.data.slice(min, max)
|
||||
return this
|
||||
}
|
||||
}
|
30
src/router.js
Normal file
30
src/router.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
function load (component) {
|
||||
return () => System.import(`components/${component}.vue`)
|
||||
}
|
||||
|
||||
export default new VueRouter({
|
||||
/*
|
||||
* NOTE! VueRouter "history" mode DOESN'T works for Cordova builds,
|
||||
* it is only to be used only for websites.
|
||||
*
|
||||
* If you decide to go with "history" mode, please also open /config/index.js
|
||||
* and set "build.publicPath" to something other than an empty string.
|
||||
* Example: '/' instead of current ''
|
||||
*
|
||||
* If switching back to default "hash" mode, don't forget to set the
|
||||
* build publicPath back to '' so Cordova builds work again.
|
||||
*/
|
||||
|
||||
routes: [
|
||||
{ path: '/', component: load('Index') }, // Default
|
||||
{ path: '/my', component: load('Myspells') },
|
||||
{ path: '/spell/:name', component: load('Spell') },
|
||||
{ path: '/about', component: load('About') },
|
||||
{ path: '*', component: load('Error404') } // Not found
|
||||
]
|
||||
})
|
10890
src/statics/dnd5e.json
Normal file
10890
src/statics/dnd5e.json
Normal file
File diff suppressed because it is too large
Load diff
68
src/statics/empty-bg.svg
Normal file
68
src/statics/empty-bg.svg
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg id="svg7404" style="enable-background:new 232 -592.3 824.9 1155;" x="0px" y="0px" viewBox="232 -592.3 824.9 1155" xmlns="http://www.w3.org/2000/svg">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EDF9F7;}
|
||||
.st1{fill:#7BCBBF;}
|
||||
.st2{fill:#9BD8D0;}
|
||||
.st3{fill:#CBE9E4;}
|
||||
.st4{fill:#6EC3BB;}
|
||||
.st5{fill:#FFFFFF;}
|
||||
.st6{fill:#EED7B2;}
|
||||
.st7{fill:#DAC4BF;}
|
||||
.st8{fill:#CCDBB9;}
|
||||
.st9{fill:#DBEFBE;}
|
||||
.st10{fill:#D4E6BC;}
|
||||
.st11{fill:#F9FBF5;}
|
||||
.st12{fill:#D6C2BE;}
|
||||
.st13{fill:#E0E4DD;}
|
||||
.st14{fill:#B3D3C8;}
|
||||
.st15{fill:#9FCABF;}
|
||||
.st16{fill:#D3DBD9;}
|
||||
.st17{fill:#A6C9C4;}
|
||||
.st18{fill:#BCD1CD;}
|
||||
.st19{fill:#92C0BB;}
|
||||
.st20{fill:#DFEAE9;}
|
||||
</style>
|
||||
<g transform="matrix(-0.992753, 0, 0, 1, 1266.810913, 17.852758)">
|
||||
<path class="st0" d="M544.6-229L544.6-229L544.6-229 M501.7-241.2L501.7-241.2l-19.1,22.8l31.1,13c0.1-1.1,0.4-2.1,1.1-2.9 		c1-1.2,2.4-1.8,4-1.8c1.4,0,2.9,0.5,4.1,1.5c0.9,0.7,1.5,1.6,1.9,2.6l19.4-23.3L501.7-241.2"/>
|
||||
<polyline class="st1" points="474.8,-209.1 447.4,-176.2 466.8,-136.4 489.6,-163.7 465.5,-197.8 474.9,-209.1 474.8,-209.1 	"/>
|
||||
<path class="st2" d="M544.5-229.4l-0.2,0.2l-19.4,23.3c0.7,1.8,0.5,3.9-0.7,5.4c-0.3,0.4-0.7,0.7-1.1,0.9l21.1,32.6l19.3-23.1 		L544.6-229l0,0L544.5-229.4"/>
|
||||
<polyline class="st3" points="489.9,-163.8 489.7,-163.6 489.6,-163.7 466.8,-136.4 466.7,-136.2 508.6,-124.3 508.6,-124.3 		527,-146.3 489.9,-163.8 	"/>
|
||||
<path class="st2" d="M482.6-218.4l-7.8,9.3l-9.4,11.3l24.2,34.1l0.1,0.1l0.2-0.2l-0.2-0.1l29.3-35c-1-0.2-2-0.7-2.9-1.4 		c-1.6-1.3-2.4-3.3-2.3-5.1L482.6-218.4L482.6-218.4"/>
|
||||
<path class="st4" d="M523.1-199.6c-0.8,0.6-1.9,0.8-2.9,0.8c-0.4,0-0.8,0-1.2-0.1l-29.3,35l0.2,0.1l37.1,17.5l8.2-9.7l9.1-10.9 		L523.1-199.6"/>
|
||||
<path class="st5" d="M518.9-210c-1.5,0-3,0.6-4,1.8c-0.7,0.8-1.1,1.9-1.1,2.9c-0.1,1.8,0.7,3.7,2.3,5.1c0.9,0.7,1.9,1.2,2.9,1.4 		c0.4,0.1,0.8,0.1,1.2,0.1c1,0,2.1-0.3,2.9-0.8c0.4-0.3,0.7-0.6,1.1-0.9c1.2-1.5,1.4-3.5,0.7-5.4c-0.4-1-1-1.9-1.9-2.6 		C521.7-209.5,520.3-210,518.9-210"/>
|
||||
<path class="st6" d="M874.4,91.9c-2,9.3-4.7,18-7.1,25.7l133.5,111.7l-2.3,2.7l0,0l13.2-15.9L874.4,91.9 M577.3-176.6l2.2,53.4 		L624-86.1c4.1-1.9,9-2.9,14.2-2.9c3.2,0,6.5,0.4,9.6,1.2c13,3.4,19.8,12.9,15,21.2c-0.3,0.5-0.6,1-1,1.5c-0.7,2.4-1.5,5.5-2.1,8.9 		L839.8,94.6c0-0.9,0-1.9-0.1-2.8l0,0c0,0,0,0,0,0c0.3-13.5,2-27.5-1.3-32.3L577.3-176.6"/>
|
||||
<path class="st7" d="M867.3,117.6c-2.8,8.9-5.1,16.4-5.1,21.9c-0.1,3.7,0.4,6.8,1.2,9.5L987.6,245l10.9-13.1l2.3-2.7L867.3,117.6 		 M659.7-56.2c-3,16.4-2.7,42.8,21.9,63.4c2.2,1.8,4.6,3.4,7,4.5l67.1,52.1l0,0c-0.5,0.2-1,0.4-1.5,0.7l87.3,67.6 		c-0.9-10.2-1.3-22.6-1.7-37.4L659.7-56.2 M579.6-123.3l0.1,2.5l-55.1,7.4L614-44.1c-0.6-5.7-0.7-10.8-0.7-15 		c0-8.5,0.7-13.6,0.7-13.6s0-0.1,0.1-0.3c-0.1-2,0.4-4,1.5-6c1.7-3,4.7-5.4,8.4-7.1L579.6-123.3"/>
|
||||
<path class="st8" d="M839.8,91.7L839.8,91.7c0,1,0,1.9,0.1,2.8c0.4,14.8,0.8,27.2,1.7,37.4c1.9,21.5,6,33.3,17.1,36.7 		c3.4,1.1,6.4,1.5,9,1.5c4.4,0,7.6-1.2,9.9-2.6c-2.3,1.4-5.5,2.6-9.9,2.6c-2.6,0-5.6-0.4-9-1.5C841.9,163.6,841,139.4,839.8,91.7 		 M806.6-3.9c-10.8,0-21.9,2-32.8,4.7l23.9,21.6c1,0,2.1-0.1,3.1-0.1c21.4,0,38,9.1,49.3,22.5c12.8,15.3,3.1,56.1,3.9,79 		c0.9,22.4-5.4,36.8,27.2,40.6c0.4-0.5,0.6-0.8,0.6-0.8s-14.1-0.8-18.4-14.8c-0.8-2.7-1.3-5.8-1.2-9.5c0.1-5.4,2.4-13,5.1-21.9 		c2.4-7.7,5.1-16.4,7.1-25.7c5.4-25.7,5-55.8-24.9-80.8C836.3,0,821.7-3.9,806.6-3.9 M613.2-59.1c0,4.2,0.2,9.3,0.7,15 		c2.5,25.9,12.6,64.3,47.8,93.6c20.4,17,39.5,23,56.7,23c13.2,0,25.2-3.5,35.8-8.1c0.5-0.2,1-0.4,1.5-0.7l0,0 		c-10.9,4.9-23.5,8.7-37.2,8.7c-17.2,0-36.3-5.9-56.7-23C618.9,13.7,613.2-35.6,613.2-59.1 M661.9-65.1c-4.1,5.4-12.5,8.6-21.6,8.6 		c-1.7,0-3.4-0.1-5.1-0.3c-0.8,25.1,5.1,61.8,29.9,82.4c10.6,8.8,25.2,11.8,40.9,11.8c4.8,0,9.7-0.3,14.7-0.8l35,27.1l-67.1-52.1 		c-2.3-1.1-4.7-2.6-7-4.5c-24.6-20.5-24.9-46.9-21.9-63.4C660.3-59.7,661.1-62.7,661.9-65.1"/>
|
||||
<path class="st9" d="M800.8,22.4c-1,0-2.1,0-3.1,0.1c0,0-0.1,0-0.1,0l40,36c0.3,0.3,0.6,0.6,0.8,0.9c3.3,4.9,1.6,18.9,1.3,32.3l0,0 		c1.2,47.7,2.2,71.8,18.9,77c3.4,1.1,6.4,1.5,9,1.5c4.4,0,7.6-1.2,9.9-2.6c1.8-1.1,3-2.3,3.7-3.1c0,0,0,0,0,0 		c-32.6-3.8-26.3-18.2-27.2-40.6c-0.8-22.9,8.9-63.7-3.9-79C838.8,31.5,822.2,22.4,800.8,22.4 M614.1-73c-0.1,0.2-0.1,0.3-0.1,0.3 		s-0.7,5.1-0.7,13.6c0,23.6,5.7,72.8,48.5,108.6c20.4,17,39.5,23,56.7,23c13.8,0,26.3-3.8,37.2-8.7l0,0l-35-27.1 		c-4.9,0.5-9.9,0.8-14.7,0.8c-15.7,0-30.3-3.1-40.9-11.8c-24.8-20.7-30.8-57.4-29.9-82.4c-1.5-0.2-3-0.5-4.5-0.9 		C620.6-60.3,614.3-66.5,614.1-73"/>
|
||||
<path class="st10" d="M638.2-59.6c-2,0-4.1-0.7-5.9-2.2c-3.7-3.1-4.4-8.2-1.7-11.4c1.4-1.7,3.5-2.5,5.7-2.5c2,0,4.1,0.7,5.9,2.2 		c3.7,3.1,4.4,8.2,1.7,11.4C642.5-60.5,640.4-59.6,638.2-59.6 M638.2-89c-5.2,0-10.1,1-14.2,2.9c-3.7,1.7-6.7,4.2-8.4,7.1 		c-1.1,2-1.6,4-1.5,6c0.3,6.5,6.6,12.7,16.5,15.3c1.5,0.4,3,0.7,4.5,0.9c1.7,0.2,3.4,0.3,5.1,0.3c9.1,0,17.5-3.2,21.6-8.6 		c0.4-0.5,0.7-1,1-1.5c4.7-8.3-2-17.8-15-21.2C644.6-88.6,641.4-89,638.2-89"/>
|
||||
<path class="st11" d="M636.3-75.8c-2.2,0-4.3,0.9-5.7,2.5c-2.7,3.3-2,8.4,1.7,11.4c1.8,1.5,3.9,2.2,5.9,2.2c2.2,0,4.3-0.9,5.7-2.5 		c2.7-3.3,2-8.4-1.7-11.4C640.5-75.1,638.4-75.8,636.3-75.8"/>
|
||||
<polyline class="st12" points="577.3,-178.5 549.7,-145.7 522.2,-113.1 524.5,-113.4 579.7,-120.8 579.6,-123.3 577.3,-176.6 		577.3,-178.5 	"/>
|
||||
<polyline class="st13" points="563.6,-190 563.5,-190 564.1,-188.9 537.9,-157.6 550.6,-147 577.1,-178.7 563.6,-190 	"/>
|
||||
<polyline class="st14" points="563.5,-190 544.2,-166.9 544.4,-166.7 537.2,-158.2 537.9,-157.6 564.1,-188.9 563.5,-190 	"/>
|
||||
<polyline class="st15" points="544.2,-166.9 535.1,-156 537,-158.3 537.2,-158.2 544.4,-166.7 544.2,-166.9 	"/>
|
||||
<polyline class="st16" points="537.9,-157.6 536.7,-156.1 536.7,-156.1 509.7,-124 508.6,-124.3 522.1,-113 550.6,-147 		537.9,-157.6 	"/>
|
||||
<polyline class="st17" points="537.2,-158.2 536.6,-157.4 537.3,-156.8 536.7,-156.1 536.7,-156.1 537.9,-157.6 537.2,-158.2 	"/>
|
||||
<polyline class="st18" points="536.6,-157.4 527.2,-146.2 527,-146.3 508.6,-124.3 508.6,-124.3 509.7,-124 536.7,-156.1 		537.3,-156.8 536.6,-157.4 	"/>
|
||||
<polyline class="st19" points="537,-158.3 535.1,-156 527,-146.3 527.2,-146.2 536.6,-157.4 537.2,-158.2 537,-158.3 	"/>
|
||||
<polygon class="st20" points="362.8,-106.5 366.8,-137.1 394.1,-151.2 366.3,-164.3 361.4,-194.7 340.3,-172.4 309.9,-177.1 		324.6,-150 310.7,-122.6 341,-128.3 	"/>
|
||||
<polygon class="st20" points="394.7,-386.8 409.4,-403.2 431.2,-400.3 420.2,-419.4 429.7,-439.2 408.2,-434.6 392.3,-449.7 		390,-427.9 370.7,-417.4 390.7,-408.5 	"/>
|
||||
<polygon class="st20" points="457,20.4 461.6,-14.7 493.1,-30.9 461.1,-46.1 455.4,-81.1 431,-55.3 396,-60.7 413,-29.6 397,2 		431.8,-4.5 	"/>
|
||||
<polygon class="st20" points="634.6,-418.3 636.7,-434.1 650.9,-441.4 636.5,-448.2 633.9,-463.9 623,-452.4 607.2,-454.8 		614.9,-440.8 607.7,-426.6 623.3,-429.5 	"/>
|
||||
<polygon class="st20" points="803.5,-10.6 800.3,-33.9 817.8,-49.6 794.6,-53.9 785,-75.3 773.8,-54.7 750.5,-52.1 766.7,-35.1 		761.9,-12.1 783.1,-22.3 	"/>
|
||||
<polygon class="st20" points="608.2,41.7 610.5,23.5 626.9,15.1 610.3,7.2 607.4,-10.9 594.7,2.5 576.6,-0.4 585.4,15.8 		577.1,32.1 595.1,28.8 	"/>
|
||||
<polygon class="st20" points="267.8,88.3 269.8,72.5 284,65.2 269.6,58.4 267,42.6 256.1,54.2 240.4,51.8 248,65.8 240.8,80 		256.5,77.1 	"/>
|
||||
<polygon class="st20" points="489.7,348 497.5,334.2 513.4,332.8 502.6,321 506.2,305.5 491.7,312.1 478,303.9 479.8,319.7 		467.8,330.2 483.4,333.4 	"/>
|
||||
<polygon class="st20" points="989.4,-441.9 997.3,-455.7 1013.1,-457.1 1002.4,-468.9 1005.9,-484.4 991.4,-477.8 977.8,-486 		979.6,-470.2 967.5,-459.7 983.2,-456.5 	"/>
|
||||
<polygon class="st20" points="934.4,137.9 939.1,126.4 951.4,123.8 941.9,115.7 943.1,103.3 932.5,109.8 921.1,104.8 924,116.9 		915.7,126.2 928.2,127.1 	"/>
|
||||
<polygon class="st20" points="326.5,108.6 324.9,96.3 334.1,87.9 321.8,85.6 316.8,74.3 310.8,85.2 298.5,86.6 307.1,95.6 		304.5,107.8 315.7,102.4 	"/>
|
||||
<polygon class="st20" points="530.6,104.7 532,93.7 541.9,88.7 531.9,83.9 530.1,73 522.5,81 511.6,79.3 516.9,89 511.9,98.9 		522.7,96.9 	"/>
|
||||
<polygon class="st20" points="742.1,-272 745.1,-282.7 755.5,-286.3 746.3,-292.4 746.1,-303.5 737.4,-296.6 726.8,-299.8 		730.7,-289.5 724.3,-280.4 735.4,-280.9 	"/>
|
||||
<polygon class="st20" points="939.7,-208.2 950.1,-211.9 959.1,-205.5 958.7,-216.6 967.5,-223.3 956.9,-226.3 953.4,-236.7 		947.2,-227.6 936.1,-227.4 943,-218.7 	"/>
|
||||
<polygon class="st20" points="825.7,-570.5 829.8,-574.5 835.4,-573.5 832.8,-578.6 835.5,-583.7 829.9,-582.8 825.9,-587 		825,-581.3 819.8,-578.8 824.9,-576.2 	"/>
|
||||
<polygon class="st20" points="862.9,475.8 867,471.8 872.6,472.8 870,467.7 872.7,462.6 867.1,463.5 863.1,459.3 862.2,465 		857,467.5 862.1,470.1 	"/>
|
||||
<polygon class="st20" points="503.3,-554 507.4,-558 513.1,-557 510.5,-562.1 513.2,-567.2 507.5,-566.3 503.6,-570.5 		502.7,-564.8 497.4,-562.3 502.6,-559.7 	"/>
|
||||
<polygon class="st20" points="706.5,302 710.6,297.9 716.3,299 713.6,293.8 716.4,288.7 710.7,289.6 706.7,285.5 705.8,291.1 		700.6,293.6 705.7,296.2 	"/>
|
||||
<polygon class="st20" points="328,488.5 333.8,487.9 337.6,492.2 338.7,486.5 344,484.2 339,481.4 338.4,475.7 334.2,479.6 		328.6,478.3 331,483.6 	"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.6 KiB |
BIN
src/statics/favicon.ico
Normal file
BIN
src/statics/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
10
src/statics/logo.svg
Normal file
10
src/statics/logo.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg x="0px" y="0px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 59.895 42.143 L 52.865 36.872 C 51.891 36.14 50.509 36.339 49.778 37.312 L 41.205 48.744 L 51.762 56.661 L 60.335 45.229 C 61.067 44.257 60.869 42.874 59.895 42.143 Z" style="fill: rgb(38, 166, 154);"/>
|
||||
<path d="M 12.836 86.568 C 12.105 87.542 12.304 88.924 13.276 89.655 L 20.306 94.926 C 21.279 95.657 22.662 95.458 23.393 94.485 L 49.385 59.827 L 38.828 51.909 L 12.836 86.568 Z" style="fill: rgb(38, 166, 154);"/>
|
||||
<polygon points="17.06 46.888 22.612 41.476 14.941 40.363 11.513 33.412 8.082 40.363 0.411 41.476 5.963 46.888 4.654 54.528 11.513 50.918 18.375 54.528" style="fill: rgb(233, 30, 99);" transform="matrix(-0.374607, -0.927184, 0.927184, -0.374607, -24.944491, 71.114733)"/>
|
||||
<polygon points="26.854 23.295 33.715 19.687 40.577 23.295 39.266 15.656 44.813 10.243 37.143 9.129 33.715 2.181 30.283 9.129 22.612 10.243 28.164 15.656" style="fill: rgb(233, 30, 99);" transform="matrix(0.992546, 0.121869, -0.121869, 0.992546, 1.803659, -4.013573)"/>
|
||||
<polygon points="66.405 77.911 62.978 70.955 59.546 77.911 51.876 79.022 57.427 84.43 56.118 92.067 62.978 88.466 69.84 92.067 68.53 84.43 74.076 79.022" style="fill: rgb(233, 30, 99);" transform="matrix(0.573576, 0.819152, -0.819152, 0.573576, 93.624363, -16.828705)"/>
|
||||
<polygon points="99.629 49.419 91.958 48.305 88.528 41.352 85.096 48.305 77.427 49.419 82.974 54.827 81.665 62.466 88.528 58.861 95.384 62.466 94.077 54.827" style="fill: rgb(233, 30, 99);" transform="matrix(0.927184, 0.374607, -0.374607, 0.927184, 25.891722, -29.383358)"/>
|
||||
<polygon points="65.895 27.583 72.758 23.978 79.619 27.583 78.309 19.944 83.859 14.534 76.189 13.422 72.758 6.47 69.329 13.422 61.66 14.534 67.211 19.944" style="fill: rgb(233, 30, 99);" transform="matrix(0.97437, -0.224951, 0.224951, 0.97437, -1.965308, 16.803712)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
49
src/store.js
Normal file
49
src/store.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { LocalStorage } from 'quasar'
|
||||
|
||||
export let state = {
|
||||
spells: {
|
||||
loaded: false,
|
||||
data: []
|
||||
},
|
||||
chosen: [],
|
||||
search: '',
|
||||
sortBy: 'name',
|
||||
previousSortBy: 'name',
|
||||
page: 1
|
||||
}
|
||||
|
||||
export function dispatch (action) {
|
||||
switch (action.type) {
|
||||
case 'SPELLS_RESOLVED':
|
||||
state.spells = action.data
|
||||
break
|
||||
case 'LOAD_LOCAL_CHOSEN' :
|
||||
state.chosen = LocalStorage.get.item('chosen').split(',')
|
||||
break
|
||||
case 'WIPE_CHOSEN':
|
||||
state.chosen = []
|
||||
LocalStorage.remove('chosen')
|
||||
break
|
||||
case 'CHANGE_CHOSEN':
|
||||
if (action.data.want) {
|
||||
state.chosen.push(action.data.name)
|
||||
}
|
||||
else {
|
||||
let index = state.chosen.indexOf(action.data.name)
|
||||
if (index >= 0) state.chosen.splice(index, 1)
|
||||
}
|
||||
|
||||
if (state.chosen.length) {
|
||||
LocalStorage.set('chosen', state.chosen.join(','))
|
||||
}
|
||||
else {
|
||||
LocalStorage.remove('chosen')
|
||||
}
|
||||
break
|
||||
case 'SEARCH_CHANGED':
|
||||
state.loadedPagination = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
export default { state, dispatch }
|
26
src/themes/app.ios.styl
Normal file
26
src/themes/app.ios.styl
Normal file
|
@ -0,0 +1,26 @@
|
|||
// This file is included in the build if src/main.js imports it.
|
||||
// Otherwise the default iOS CSS file is bundled.
|
||||
// Check "DEFAULT / CUSTOM STYLE" in src/main.js
|
||||
|
||||
// App Shared Variables
|
||||
// --------------------------------------------------
|
||||
// Shared Stylus variables go in the app.variables.styl file
|
||||
@import 'app.variables'
|
||||
|
||||
// App iOS Design Variables
|
||||
// --------------------------------------------------
|
||||
// iOS Design only Stylus variables can go here
|
||||
// http://quasar-framework.org/api/css-stylus-variables.html
|
||||
|
||||
$typography-font-family ?= '-apple-system', 'Helvetica Neue', Helvetica, Arial, sans-serif
|
||||
|
||||
$toolbar-color ?= #424242
|
||||
$toolbar-background ?= $light
|
||||
$toolbar-active-color ?= $light
|
||||
$toolbar-faded-color ?= composite-color($toolbar-color)
|
||||
|
||||
// Quasar iOS Design Stylus
|
||||
// --------------------------------------------------
|
||||
// Custom App variables must be declared before importing Quasar.
|
||||
// Quasar will use its default values when a custom variable isn't provided.
|
||||
@import '~quasar-framework/dist/quasar.ios.styl'
|
26
src/themes/app.mat.styl
Normal file
26
src/themes/app.mat.styl
Normal file
|
@ -0,0 +1,26 @@
|
|||
// This file is included in the build if src/main.js imports it.
|
||||
// Otherwise the default Material CSS file is bundled.
|
||||
// Check "DEFAULT / CUSTOM STYLE" in src/main.js
|
||||
|
||||
// App Shared Variables
|
||||
// --------------------------------------------------
|
||||
// Shared Stylus variables go in the app.variables.styl file
|
||||
@import 'app.variables'
|
||||
|
||||
// App Material Design Variables
|
||||
// --------------------------------------------------
|
||||
// Material Design only Stylus variables can go here
|
||||
// http://quasar-framework.org/api/css-stylus-variables.html
|
||||
|
||||
$typography-font-family ?= 'Roboto'
|
||||
|
||||
$toolbar-color ?= white
|
||||
$toolbar-background ?= $primary
|
||||
$toolbar-active-color ?= $primary
|
||||
$toolbar-faded-color ?= composite-color($primary)
|
||||
|
||||
// Quasar Material Design Stylus
|
||||
// --------------------------------------------------
|
||||
// Custom App variables must be declared before importing Quasar.
|
||||
// Quasar will use its default values when a custom variable isn't provided.
|
||||
@import '~quasar-framework/dist/quasar.mat.styl'
|
37
src/themes/app.variables.styl
Normal file
37
src/themes/app.variables.styl
Normal file
|
@ -0,0 +1,37 @@
|
|||
// This file is included in the build if src/main.js imports
|
||||
// either app.mat.styl or app.ios.styl.
|
||||
// Check "DEFAULT / CUSTOM STYLE" in src/main.js
|
||||
|
||||
// App Shared Variables
|
||||
// --------------------------------------------------
|
||||
// To customize the look and feel of this app, you can override
|
||||
// the Stylus variables found in Quasar's source Stylus files. Setting
|
||||
// variables before Quasar's Stylus will use these variables rather than
|
||||
// Quasar's default Stylus variable values. Stylus variables specific
|
||||
// to the themes belong in either the app.ios.styl or app.mat.styl files.
|
||||
|
||||
|
||||
// App Shared Color Variables
|
||||
// --------------------------------------------------
|
||||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding.
|
||||
|
||||
$primary = #027be3
|
||||
$secondary = #26A69A
|
||||
$tertiary = #555
|
||||
|
||||
$neutral = #E0E1E2
|
||||
$positive = #21BA45
|
||||
$negative = #DB2828
|
||||
$info = #31CCEC
|
||||
$warning = #F2C037
|
||||
|
||||
$light = #f4f4f4
|
||||
$dark = #333
|
||||
$faded = #777
|
||||
|
||||
$text-color = lighten(black, 17%)
|
||||
$background-color = white
|
||||
|
||||
$link-color = lighten($primary, 25%)
|
||||
$link-color-active = $primary
|
1
src/utils.js
Normal file
1
src/utils.js
Normal file
|
@ -0,0 +1 @@
|
|||
export let capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
|
14
templates/component.vue
Normal file
14
templates/component.vue
Normal file
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
55
templates/layout.vue
Normal file
55
templates/layout.vue
Normal file
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<div slot="header" class="toolbar">
|
||||
<!-- opens drawer below
|
||||
<button class="hide-on-drawer-visible" @click="$refs.drawer.open()">
|
||||
<i>menu</i>
|
||||
</button>
|
||||
-->
|
||||
<q-toolbar-title :padding="1">
|
||||
Title
|
||||
</q-toolbar-title>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Tabs
|
||||
<q-tabs slot="navigation">
|
||||
<q-tab icon="mail" route="/layout" exact replace>Mails</q-tab>
|
||||
<q-tab icon="alarm" route="/layout/alarm" exact replace>Alarms</q-tab>
|
||||
<q-tab icon="help" route="/layout/help" exact replace>Help</q-tab>
|
||||
</q-tabs>
|
||||
-->
|
||||
|
||||
<!-- Drawer
|
||||
<q-drawer ref="drawer">
|
||||
<div class="toolbar">
|
||||
<q-toolbar-title>
|
||||
Drawer Title
|
||||
</q-toolbar-title>
|
||||
</div>
|
||||
|
||||
<div class="list no-border platform-delimiter">
|
||||
<q-drawer-link icon="mail" :to="{path: '/', exact: true}">
|
||||
Link
|
||||
</q-drawer-link>
|
||||
</div>
|
||||
</q-drawer>
|
||||
-->
|
||||
|
||||
<router-view class="layout-view"></router-view>
|
||||
|
||||
<!-- Footer
|
||||
<div slot="footer" class="toolbar"></div>
|
||||
-->
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
20
templates/view.vue
Normal file
20
templates/view.vue
Normal file
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<!-- root node required -->
|
||||
<div>
|
||||
<!-- your content -->
|
||||
<div class="layout-padding">
|
||||
<!-- if you want automatic padding -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
Loading…
Add table
Reference in a new issue