├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── app
├── App.vue
├── Minesweeper
│ ├── Cell.vue
│ ├── Minesweeper.css
│ ├── Minesweeper.vue
│ └── sprite.png
├── boot.mjs
└── util
│ ├── Deferred.mjs
│ ├── filters.mjs
│ └── util.mjs
├── package-lock.json
├── package.json
├── webpack.config.base.js
├── webpack.config.js
└── www
├── favicon.ico
└── index.html
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "production": {
4 | "presets": [
5 | ["es2015", {"modules": false}]
6 | ],
7 | "comments": false
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "plugin:vue/recommended",
4 | "standard"
5 | ],
6 | "rules": {
7 | "prefer-const": "error",
8 | "no-var": "error",
9 | "prefer-template": "error",
10 | "vue/require-prop-types": "off",
11 | "vue/require-default-prop": "off",
12 | "vue/max-attributes-per-line": [
13 | "error",
14 | {
15 | "singleline": 5,
16 | "multiline": {
17 | "max": 5,
18 | "allowFirstLine": true
19 | }
20 | }
21 | ],
22 | "vue/singleline-html-element-content-newline": "off",
23 | "vue/component-name-in-template-casing": "off"
24 | },
25 | "plugins": [
26 | "vue"
27 | ],
28 | "globals": {
29 | "_": true,
30 | "Vue": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_STORE
3 | .netbeans
4 | .node_history
5 | .vscode
6 | .tscache
7 | .idea
8 | build
9 | www/**
10 | !www/favicon.ico
11 | !www/index.html
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue Minesweeper
2 | Vue based classic Minesweeper 🚩 [Try Demo](https://elevista.github.io/vue-minesweeper)
3 |
4 | 
5 | 
6 |
7 | (`electron` branch)
8 |
9 | [https://github.com/elevista/vue-minesweeper](https://github.com/elevista/vue-minesweeper)
10 |
11 | ## License
12 | The MIT License (MIT)
13 |
14 | Copyright (c) 2018 Elevista
15 |
--------------------------------------------------------------------------------
/app/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
58 |
129 |
--------------------------------------------------------------------------------
/app/Minesweeper/Cell.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
45 |
74 |
--------------------------------------------------------------------------------
/app/Minesweeper/Minesweeper.css:
--------------------------------------------------------------------------------
1 | .minesweeper {
2 | display: inline-block;
3 | background-color: silver;
4 | border: outset 2px #eee;
5 | padding: 6px;
6 | }
7 | .board {border: inset 2px #eee;}
8 | .board .row {
9 | height: 16px;
10 | white-space: nowrap;
11 | }
12 | .indicator {
13 | height: 25px;
14 | border: inset 2px #eee;
15 | padding: 4px 6px;
16 | text-align: center;
17 | margin-bottom: 6px;
18 | }
19 | .indicator * {background-repeat: no-repeat;}
20 | .indicator > * {color: transparent;}
21 | .indicator .smiley {
22 | width: 26px;
23 | height: 26px;
24 | display: inline-block;
25 | border: none;
26 | outline: none;
27 | margin: 0;
28 | padding: 0;
29 | background: url(sprite.png) no-repeat;
30 | background-position-y: -55px;
31 | background-position-x: 0;
32 | }
33 | .indicator .smiley.ooh {background-position-x: -52px;}
34 | .indicator .smiley.dead {background-position-x: -78px;}
35 | .indicator .smiley.win {background-position-x: -104px;}
36 | .indicator .smiley:active {background-position-x: -26px;}
37 | .indicator .left {float: left;}
38 | .indicator .right {float: right;}
39 | .indicator .count {
40 | width: 13px;
41 | height: 23px;
42 | display: inline-block;
43 | background: url(sprite.png) no-repeat;
44 | background-position-y: 0;
45 | }
46 | .indicator .count.n0 {background-position-x: 0;}
47 | .indicator .count.n1 {background-position-x: -13px;}
48 | .indicator .count.n2 {background-position-x: -26px;}
49 | .indicator .count.n3 {background-position-x: -39px;}
50 | .indicator .count.n4 {background-position-x: -52px;}
51 | .indicator .count.n5 {background-position-x: -65px;}
52 | .indicator .count.n6 {background-position-x: -78px;}
53 | .indicator .count.n7 {background-position-x: -91px;}
54 | .indicator .count.n8 {background-position-x: -104px;}
55 | .indicator .count.n9 {background-position-x: -117px;}
56 | .indicator .count.n- {background-position-x: -130px;}
57 |
--------------------------------------------------------------------------------
/app/Minesweeper/Minesweeper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ x }}
7 |
8 |
9 |
10 | {{ x }}
11 |
12 |
13 |
23 |
24 |
25 |
172 |
173 |
--------------------------------------------------------------------------------
/app/Minesweeper/sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Elevista/vue-minesweeper/020d1c12fdce5237e67a664d6d78be7ea1893fea/app/Minesweeper/sprite.png
--------------------------------------------------------------------------------
/app/boot.mjs:
--------------------------------------------------------------------------------
1 | import App from './App.vue'
2 | import Vue from 'vue'
3 | import './util/filters.mjs'
4 | import _ from 'lodash'
5 | import { lodashMixin } from './util/util.mjs'
6 |
7 | _.mixin(lodashMixin)
8 |
9 | new Vue({ el: 'app', render: h => h(App) }) // eslint-disable-line no-new
10 |
--------------------------------------------------------------------------------
/app/util/Deferred.mjs:
--------------------------------------------------------------------------------
1 | export function Deferred () {
2 | this.promise = new Promise((resolve, reject) => Object.assign(this, { resolve, reject }))
3 | }
4 |
--------------------------------------------------------------------------------
/app/util/filters.mjs:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import _ from 'lodash'
3 |
4 | Vue.filter('numberComma', n => `${n || 0}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','))
5 | _.forEach(
6 | [
7 | 'ceil',
8 | 'floor',
9 | 'max',
10 | 'maxBy',
11 | 'mean',
12 | 'meanBy',
13 | 'min',
14 | 'minBy',
15 | 'round',
16 | 'sum',
17 | 'sumBy',
18 | 'clamp',
19 | 'camelCase',
20 | 'capitalize',
21 | 'endsWith',
22 | 'escape',
23 | 'escapeRegExp',
24 | 'kebabCase',
25 | 'lowerCase',
26 | 'lowerFirst',
27 | 'pad',
28 | 'padEnd',
29 | 'padStart',
30 | 'parseInt',
31 | 'repeat',
32 | 'replace',
33 | 'snakeCase',
34 | 'split',
35 | 'startCase',
36 | 'startsWith',
37 | 'template',
38 | 'toLower',
39 | 'toUpper',
40 | 'trim',
41 | 'trimEnd',
42 | 'trimStart',
43 | 'truncate',
44 | 'unescape',
45 | 'upperCase',
46 | 'upperFirst',
47 | 'words'
48 | ],
49 | fn => Vue.filter(fn, _[fn])
50 | )
51 |
--------------------------------------------------------------------------------
/app/util/util.mjs:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 |
3 | export function sum (a, b) {
4 | return a + b
5 | }
6 |
7 | const lodash = _.runInContext()
8 | export const lodashMixin = _(['pull', 'pullAll', 'pullAllBy', 'pullAllWith', 'pullAt', 'remove'])
9 | .map(fnName => {
10 | const fn = lodash[fnName]
11 | return [
12 | fnName,
13 | function (v, ...args) {
14 | const ret = fn(v, ...args)
15 | if (v instanceof Array) v.push()
16 | return ret
17 | }]
18 | })
19 | .fromPairs()
20 | .value()
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-minesweeper",
3 | "private": true,
4 | "version": "0.0.1",
5 | "description": "Vue Minesweeper",
6 | "keywords": [],
7 | "devDependencies": {
8 | "css-loader": "^2.1.0",
9 | "eslint-plugin-vue": "^5.1.0",
10 | "file-loader": "^3.0.1",
11 | "lodash": "^4.17.11",
12 | "push-dir": "^0.4.1",
13 | "standard": "^12.0.1",
14 | "style-loader": "^0.23.1",
15 | "vue": "^2.5.22",
16 | "vue-loader": "^15.5.1",
17 | "vue-template-compiler": "^2.5.22",
18 | "webpack": "^4.29.0",
19 | "webpack-cli": "^3.2.1",
20 | "webpack-dev-server": "^3.1.14"
21 | },
22 | "scripts": {
23 | "start": "webpack-dev-server --content-base www/ --hot",
24 | "build": "webpack",
25 | "build:prod": "webpack --mode=production",
26 | "gh-pages": "npm run build:prod && push-dir --dir=www --branch=gh-pages --cleanup",
27 | "lint": "eslint --ext .js,.mjs,.vue . --fix"
28 | },
29 | "main": "app.js",
30 | "repository": {
31 | "type": "git"
32 | },
33 | "author": {
34 | "name": "Elevista",
35 | "email": "sunnyholic@sunnyholic.com",
36 | "homepage": "http://sunnyholic.com"
37 | },
38 | "eslintIgnore": ["node_modules", "www", "build"],
39 | "license": ""
40 | }
41 |
--------------------------------------------------------------------------------
/webpack.config.base.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const VueLoaderPlugin = require('vue-loader/lib/plugin')
4 | module.exports = function (name) {
5 | return {
6 | mode: 'development',
7 | name,
8 | module: {
9 | rules: [
10 | { test: /\.vue$/, loader: 'vue-loader' },
11 | { test: /\.css$/, loader: 'vue-style-loader!css-loader' },
12 | { test: /\.(png|woff|woff2|eot|ttf|svg|jpg|otf|gif)$/, loader: 'file-loader?outputPath=files/' }
13 | ]
14 | },
15 | plugins: [new VueLoaderPlugin(), new webpack.ProvidePlugin({ _: 'lodash', Vue: 'vue' })],
16 | resolve: { alias: { '~': path.resolve(__dirname, `${name}`) } }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const baseConf = require('./webpack.config.base')
3 |
4 | module.exports = function (env, argv = {}) {
5 | const sourceMapFileNameTemplate = info => `webpack:///${info.resourcePath.replace(/\.vue$/, '.vue.html')}`
6 | const sourceMapFileNameDupTemplate = info => sourceMapFileNameTemplate(info) + info.query
7 |
8 | const config = Object.assign(baseConf('app'), {
9 | entry: './app/boot.mjs',
10 | devtool: argv.mode === 'production' ? false : 'inline-source-map',
11 | output: {
12 | filename: 'bundle.js',
13 | path: path.resolve(__dirname, 'www/'),
14 | devtoolModuleFilenameTemplate: sourceMapFileNameTemplate,
15 | devtoolFallbackModuleFilenameTemplate: sourceMapFileNameDupTemplate
16 | }
17 | })
18 | return config
19 | }
20 |
--------------------------------------------------------------------------------
/www/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Elevista/vue-minesweeper/020d1c12fdce5237e67a664d6d78be7ea1893fea/www/favicon.ico
--------------------------------------------------------------------------------
/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Vue Minesweeper
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------