Commit 9b04a859 authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt

update javascript tooling

parent f0f6a159
Pipeline #2044 passed with stage
in 8 minutes and 10 seconds
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/recommended'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'indent': 'off',
'vue/script-indent': [
'warn',
2,
{
'baseIndent': 1,
'switchCase': 1
}
]
},
parserOptions: {
parser: 'babel-eslint'
}
}
......@@ -4,7 +4,6 @@ module.exports = ({ file, options }) => {
return {
plugins: {
'autoprefixer': isDebug ? false : options.autoprefixer || {},
'cssnano': isDebug ? false : options.cssnano || {},
'postcss-custom-properties': isDebug ? false : Object.assign({}, {
preserve: true
})
......
BIN_NODE_SYSTEM = $(shell which nodejs node | head -1)
BIN_NPM_SYSTEM = $(shell which npm || true)
BIN_NODE_SYSTEM = $(shell command -v nodejs node | head -1)
BIN_NPM_SYSTEM = $(shell command -v npm || true)
NODE_LOCAL_DIR = $(DIR_BUILD)/node
BIN_NODE ?= $(abspath $(NODE_LOCAL_DIR))/bin/node
BIN_NPM ?= $(abspath $(NODE_LOCAL_DIR))/bin/npm
......@@ -26,7 +26,7 @@ $(BIN_NODE):
echo >&2 "NodeJS download for non-linux platforms is sadly not supported. Please install $(NODE_VERSION_MIN) or later manually."; \
exit 1; fi; \
echo >&2 "Local nodejs version is missing or too old (before $(NODE_VERSION_MIN)) or 'npm' is missing. Downloading from server ..."; \
if [ -n "$(shell which xzcat)" ]; then \
if [ -n "$(shell command -v xzcat)" ]; then \
wget -O - "$(NODE_URL)" | tar -xJ -C "$(NODE_LOCAL_DIR)" --strip-components=1 -f -; \
else \
echo >&2 "Failed to install nodejs due to missing dependency: xz compression tool (Debian: package 'xz-utils')"; \
......
This diff is collapsed.
......@@ -8,62 +8,65 @@
},
"scripts": {
"test": "true",
"lint": "standard 'res/**/*.{js,vue}' 'webpack/**/*.js'"
"lint": "eslint 'res/**/*.{js,vue}' 'webpack/**/*.js'"
},
"repository": {
"type": "git",
"url": "https://git.hack-hro.de/stadtgestalten/stadtgestalten.git"
},
"devDependencies": {
"autoprefixer": "^7.2.6",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"@babel/core": "^7.7.4",
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
"@babel/plugin-transform-async-to-generator": "^7.7.4",
"@babel/plugin-transform-regenerator": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.4",
"@babel/plugin-transform-unicode-regex": "^7.7.4",
"@babel/preset-env": "^7.7.4",
"autoprefixer": "^9.7.3",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
"babel-plugin-transform-regenerator": "^6.26.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-plugin-transform-remove-debugger": "^6.9.4",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-transform-xregexp": "0.0.6",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.22.0",
"babel-root-slash-import": "^1.1.0",
"css-loader": "^0.28.11",
"csswring": "v4.2.3",
"eslint-plugin-html": "^3.1.1",
"extract-text-webpack-plugin": "^3.0.2",
"favicons-webpack-plugin": "0.0.9",
"file-loader": "^1.1.11",
"babel-plugin-transform-xregexp": "^1.0.0",
"core-js": "^3.4.7",
"css-loader": "^3.2.1",
"csswring": "v7.0.0",
"eslint": "^6.7.2",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-html": "^6.0.0",
"eslint-plugin-vue": "^6.0.1",
"favicons-webpack-plugin": "1.0.2",
"file-loader": "^5.0.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^2.30.1",
"html-webpack-plugin": "^4.0.0-beta.11",
"json-loader": "^0.5.7",
"less": "^2.7.3",
"less-loader": "^4.1.0",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"lodash-webpack-plugin": "^0.11.5",
"mini-css-extract-plugin": "^0.8.0",
"modernizr-loader": "^1.0.1",
"postcss": "^6.0.23",
"postcss-banner": ">=1",
"postcss-custom-properties": "^6.3.1",
"postcss-loader": "^2.1.6",
"standard": "^10.0.2",
"svgo": "^0.7.2",
"svgo-loader": "^1.2.1",
"vue-loader": "^13.7.3",
"vue-template-compiler": "^2.5.21",
"webpack": "^3.12.0",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-bundle-size-analyzer": "^2.7.0",
"webpack-pwa-manifest": "^3.8.0"
"postcss": "^7.0.23",
"postcss-banner": ">=3",
"postcss-custom-properties": "^9.0.2",
"postcss-loader": "^3.0.0",
"standard": "^14.3.1",
"svgo": "^1.3.2",
"svgo-loader": "^2.2.1",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-loader": "^15.7.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.41.2",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-bundle-size-analyzer": "^3.1.0",
"webpack-cli": "^3.3.10",
"webpack-pwa-manifest": "^4.1.1"
},
"dependencies": {
"@babel/runtime": "^7.7.4",
"animejs": "^2.1.8",
"autosize": "^4.0.2",
"axios": "^0.16.2",
"babel-runtime": "^6.20.0",
"axios": "^0.19.0",
"bel": "^5.1.7",
"bootstrap": "^3.4.0",
"bowser": "^1.9.4",
......@@ -72,79 +75,58 @@
"cookie_js": "^1.2.0",
"delegate": "^3.2.0",
"emoji-regex": "^6.5.0",
"es6-promise": "^4.2.5",
"es6-promise": "^4.2.8",
"eventemitter3": "^2.0.0",
"flatpickr": "^3.0.6",
"font-awesome": "^4.5.0",
"font-awesome": "^4.7.0",
"get-input-selection": "^1.1.4",
"imagesloaded": "^4.1.4",
"lodash": "^4.17.11",
"lodash": "^4.17.15",
"lodash-move": "^1.1.1",
"luett": "^2.6.0",
"masonry-layout": "^4.2.2",
"matches-selector-polyfill": "^1.0.0",
"modernizr": "^3.6.0",
"moment": "^2.23.0",
"modernizr": "^3.8.0",
"moment": "^2.24.0",
"node-fontdump": "^2.1.0",
"object-assign-shim": "^1.0.0",
"photoswipe": "^4.1.2",
"popper.js": "^1.14.6",
"postcss-cli": "^4.1.0",
"photoswipe": "^4.1.3",
"popper.js": "^1.16.0",
"postcss-cli": "^6.1.3",
"postcss-less-engine": "^0.6.2",
"q": "^1.5.1",
"random-color": "^1.0.1",
"random-id": "^0.0.2",
"simplemde": "git://github.com/stadtgestalten/simplemde-markdown-editor.git#translatable-prompt-texts",
"stroll.js": "^3.0.3",
"tether": "^1.4.5",
"tether": "^1.4.7",
"tether-drop": "^1.4.2",
"tether-tooltip": "^1.2.0",
"vue": "^2.5.21",
"vue": "^2.6.10",
"vue-on-click-outside": "^1.0.3",
"vue-tabs-component": "git+https://github.com/stadtgestalten/vue-tabs-component.git#dist",
"vuedraggable": "^2.17.0"
"vuedraggable": "^2.23.2"
},
"babel": {
"plugins": [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-async-to-generator",
"@babel/plugin-transform-regenerator",
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-unicode-regex",
"lodash",
"syntax-dynamic-import",
"transform-async-to-generator",
"transform-es2015-unicode-regex",
"transform-regenerator",
"transform-runtime",
"transform-xregexp"
],
"presets": [
"stage-2",
[
"env",
"@babel/preset-env",
{
"loose": true,
"useBuiltIns": true,
"modules": false,
"targets": {
"browsers": [
"last 2 versions",
"> 4%",
"Firefox ESR",
"IE 11"
]
}
"useBuiltIns": "usage",
"corejs": 3
}
]
]
},
"standard": {
"parser": "babel-eslint",
"envs": [
"browser",
"node",
"mocha"
],
"plugins": [
"html"
]
},
"browserslist": [
"last 2 versions",
"> 4%",
......
import { includes, isPlainObject } from 'lodash'
import { cookie } from 'cookie_js'
import cookie from 'cookie_js'
import axios from 'axios'
const noop = config => config
......
import { each, has, pick, defaults, mapKeys, mapValues, pickBy } from 'lodash'
import { cookie } from 'cookie_js'
import cookie from 'cookie_js'
import decorator from './_decorator'
const numberOrNull = numeric => {
......
<template>
<div class="content-meta">
<div class="content-meta-avatar">
<sg-avatar :entity="choice"></sg-avatar>
</div>
<div class="content-meta-info">
<div class="content-meta-author">{{ choice.name }}</div>
<div class="content-meta-extra" v-if="choice.tags">{{ tags }}</div>
</div>
<div class="content-meta">
<div class="content-meta-avatar">
<sg-avatar :entity="choice" />
</div>
<div class="content-meta-info">
<div class="content-meta-author">
{{ choice.name }}
</div>
<div
v-if="choice.tags"
class="content-meta-extra"
>
{{ tags }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
choice: {
choice: Object,
type: Object,
required: true
}
},
......
......@@ -3,9 +3,18 @@
<span class="btn-toolbar-label">Sortierung:</span>
<template v-for="action in actions">
<button type="button" class="btn btn-icon btn-sm" :aria-pressed="action.pressed"
:title="action.title" @click="select(action.value)">
<i class="sg" :class="`sg-${action.icon}`"></i>
<button
:key="action.value"
type="button"
class="btn btn-icon btn-sm"
:aria-pressed="action.pressed"
:title="action.title"
@click="select(action.value)"
>
<i
class="sg"
:class="`sg-${action.icon}`"
/>
</button>
</template>
</div>
......@@ -14,7 +23,10 @@
<script>
export default {
props: {
value: String
value: {
type: String,
required: true
}
},
computed: {
actions () {
......
<template>
<a class="group-media" :href="group.url">
<a
class="group-media"
:href="group.url"
>
<header class="group-media-image">
<sg-avatar :size="64" :entity="group" />
<sg-avatar
:size="64"
:entity="group"
/>
</header>
<div class="group-media-body">
<h3>{{ group.name }}</h3>
......
<template>
<transition name="fade-down">
<div class="group-preview" v-show="showPreview"
@mouseenter="setHovered(true)" @mouseleave="setHovered(false)">
<div
v-show="showPreview"
class="group-preview"
@mouseenter="setHovered(true)"
@mouseleave="setHovered(false)"
>
<header class="group-preview-header">
<div class="group-preview-image" :class="{'group-preview-image-placeholder': !group.cover}">
<img :src="group.cover" v-if="group.cover" alt="">
<div
class="group-preview-image"
:class="{'group-preview-image-placeholder': !group.cover}"
>
<img
v-if="group.cover"
:src="group.cover"
alt=""
>
</div>
<div class="avatar-wrap group-avatar">
<sg-avatar :size="64" :entity="group"></sg-avatar>
<sg-avatar
:size="64"
:entity="group"
/>
</div>
</header>
<div class="group-preview-body">
......@@ -20,12 +34,6 @@
<script>
export default {
data () {
return {
isHovered: false,
isHoveredDelay: null
}
},
props: {
group: Object,
visibilityDelay: {
......@@ -37,6 +45,12 @@
default: true
}
},
data () {
return {
isHovered: false,
isHoveredDelay: null
}
},
computed: {
showPreview () {
return this.visible || this.isHovered
......
<template>
<div class="form form-modern search">
<sg-input type="search" v-model="searchTerm" label="Gruppen suchen..."
:groupClasses="{'search-loading': isSearching }"
:inputClasses="['search-input']"></sg-input>
<div v-if="!searchTerm" v-html="defaultResults"></div>
<div class="search-results" v-else-if="hasResults">
<group-preview :group="group" :key="group.id" v-for="group in results" />
<sg-input
v-model="searchTerm"
type="search"
label="Gruppen suchen..."
:group-classes="{'search-loading': isSearching }"
:input-classes="['search-input']"
/>
<div
v-if="!searchTerm"
v-html="defaultResults"
/>
<div
v-else-if="hasResults"
class="search-results"
>
<group-preview
v-for="group in results"
:key="group.id"
:group="group"
/>
</div>
<div v-else-if="hasNoResults">
<div class="alert alert-block">Nix g’scheites bei 😞</div>
<div class="alert alert-block">
Nix g’scheites bei 😞
</div>
</div>
</div>
</template>
......
<template>
<div class="avatar-wrap" :title="entity.name" :class="{ 'avatar-inline': inline }">
<div class="avatar" :class="avatarClasses" v-if="entity.avatar !== null">
<img :src="entity.avatar" alt="">
</div>
<div class="avatar avatar-initials" :class="avatarClasses" :data-initials="entity.initials.length" :style="avatarStyle" v-else>
<span v-if="entity.initials.length <= 4">
{{ entity.initials }}
</span>
<span v-else>
{{ entity.initials.substr(0, 1) }}...
</span>
</div>
<div
class="avatar-wrap"
:title="entity.name"
:class="{ 'avatar-inline': inline }"
>
<div
v-if="entity.avatar !== null"
class="avatar"
:class="avatarClasses"
>
<img
:src="entity.avatar"
alt=""
>
</div>
<div
v-else
class="avatar avatar-initials"
:class="avatarClasses"
:data-initials="entity.initials.length"
:style="avatarStyle"
>
<span v-if="entity.initials.length <= 4">
{{ entity.initials }}
</span>
<span v-else>
{{ entity.initials.substr(0, 1) }}...
</span>
</div>
</div>
</template>
<script>
......
<template>
<svg viewBox="-1 -1 2 2" class="chart chart-pie" :style="style">
<svg
viewBox="-1 -1 2 2"
class="chart chart-pie"
:style="style"
>
<g transform="rotate(-90)">
<g class="chart-arc" v-for="(item, index) in data">
<g
v-for="(item, index) in data"
:key="index"
class="chart-arc"
>
<title v-if="item.title">{{ item.title }}</title>
<path :d="calculateArc(item, index)" :class="item.classes" />
<path
:d="calculateArc(item, index)"
:class="item.classes"
/>
</g>
</g>
</svg>
......
<template>
<div class="configurator" ref="configurator">
<div class="configurator-label" tabindex="0"
@click.stop="toggle(true)" @keydown.enter="toggle()" @focus="toggle(true)">
<slot name="label"></slot>
<div
ref="configurator"
class="configurator"
>
<div
class="configurator-label"
tabindex="0"
@click.stop="toggle(true)"
@keydown.enter="toggle()"
@focus="toggle(true)"
>
<slot name="label" />
</div>
<transition name="fade-down">
<div class="configurator-popup" v-if="opened" v-on-click-outside="dismiss">
<div
v-if="opened"
v-on-click-outside="dismiss"
class="configurator-popup"
>
<header class="configurator-header">
<slot name="icon"></slot>
<slot name="icon" />
<h3 class="configurator-title">
<slot name="title"></slot>
<slot name="title" />
</h3>
<div class="configurator-modifiers">
<slot name="modifiers"></slot>
<slot name="modifiers" />
</div>
</header>
<div class="configurator-body">
<slot></slot>
<slot />
</div>
<footer class="configurator-footer">
<slot name="footer">
<div class="btn-toolbar btn-toolbar-right">
<slot name="actions">
<button type="button" class="btn btn-link btn-sm" @click="abort">Abbrechen</button>
<button type="button" class="btn btn-primary btn-sm" @click="save">Speichern</button>
<button
type="button"
class="btn btn-link btn-sm"
@click="abort"
>
Abbrechen
</button>
<button
type="button"
class="btn btn-primary btn-sm"
@click="save"
>
Speichern
</button>
</slot>
</div>
</slot>
......@@ -54,6 +78,12 @@
opened: false
}
},
mounted () {
focusInListener.register(this)
},
beforeDestroy () {
focusInListener.remove(this)
},
methods: {
dismiss () {
this.abort()
......@@ -73,12 +103,6 @@
this.opened = newState
}
}
},
mounted () {
focusInListener.register(this)
},
beforeDestroy () {
focusInListener.remove(this)
}
}
</script>
<template>
<div>
<button type="button" :class="btnClasses" @click.prevent="open" :disabled="disabled">
<i class="sg" :class="btnIcon"></i> {{ btnLabel }}
<button
type="button"
:class="btnClasses"
:disabled="disabled"
@click.prevent="open"
>
<i
class="sg"
:class="btnIcon"
/> {{ btnLabel }}
</button>
<input type="file" :accept="accept.join(',')" :multiple="multiple" @change="dispatch" ref="input"
style="position: absolute; z-index: -1; opacity: 0; pointer-events: none; height: 0; width: 0">
<input
ref="input"
type="file"
:accept="accept.join(',')"
:multiple="multiple"
style="position: absolute; z-index: -1; opacity: 0; pointer-events: none; height: 0; width: 0"
@change="dispatch"
>
</div>
</template>
......
<template>
<div class="form-group" :class="_groupClasses">
<label class="control-label" :for="id" v-if="label">{{ label }}</label>
<input class="form-control vue-input" :class="inputClasses" :id="id" v-model="currentValue"
ref="input"
@focus="hasFocus = true; hadFocus = true" @blur="hasFocus = false"
@input="hasChanged = true">
<div
class="form-group"
:class="_groupClasses"
>
<label
v-if="label"
class="control-label"
:for="id"
>{{ label }}</label>
<input
:id="id"
ref="input"
v-model="currentValue"
class="form-control vue-input"
:class="inputClasses"
@focus="hasFocus = true; hadFocus = true"
@blur="hasFocus = false"
@input="hasChanged = true"
>
</div>
</template>
...