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
|
.DS_Store
|
||||||
logs
|
node_modules/
|
||||||
*.log
|
dist/
|
||||||
npm-debug.log*
|
npm-debug.log
|
||||||
|
npm-debug.log.*
|
||||||
# Runtime data
|
selenium-debug.log
|
||||||
pids
|
test/unit/coverage
|
||||||
*.pid
|
test/e2e/reports
|
||||||
*.seed
|
cordova/platforms
|
||||||
|
cordova/plugins
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
thumbs.db
|
||||||
lib-cov
|
!.gitkeep
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
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