mirror of
https://github.com/aelve/guide.git
synced 2024-12-22 20:31:31 +03:00
Front/chore/migrate to vuetify 2.0 (#348)
* Vuetify updated * Correct component creation in dialog mixins * Vuetify 2.0 migration * Conflict dialog content style fixed * Removed duplicate attribute
This commit is contained in:
parent
41c3523323
commit
9e4146b5bc
@ -4,18 +4,12 @@ module.exports = {
|
||||
targets: {
|
||||
browsers: ['last 3 versions', '> 2%', 'ie >= 10', 'Firefox >= 30', 'Chrome >= 30']
|
||||
},
|
||||
modules: false,
|
||||
loose: true,
|
||||
useBuiltIns: 'entry'
|
||||
modules: 'commonjs',
|
||||
useBuiltIns: 'usage'
|
||||
}]
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-syntax-dynamic-import'
|
||||
],
|
||||
env: {
|
||||
test: {
|
||||
plugins: ['transform-es2015-modules-commonjs']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ const path = require('path')
|
||||
const { DefinePlugin } = require('webpack')
|
||||
const { VueLoaderPlugin } = require('vue-loader')
|
||||
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
|
||||
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
@ -50,14 +51,13 @@ const config = {
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
},
|
||||
// exclude: /node_modules\/(?!(vuetify)\/).*/
|
||||
exclude: /(node_modules)/
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader'
|
||||
},
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
@ -70,9 +70,6 @@ const config = {
|
||||
{
|
||||
test: /\.tsx$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader'
|
||||
},
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
@ -125,32 +122,16 @@ const config = {
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
test: /\.s(c|a)ss$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'vue-style-loader',
|
||||
options: {
|
||||
sourceMap: false,
|
||||
shadowMode: false
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: false,
|
||||
importLoaders: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
sourceMap: false
|
||||
}
|
||||
},
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: false
|
||||
implementation: require('sass'),
|
||||
fiber: require('fibers'),
|
||||
indentedSyntax: true // optional
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -188,7 +169,8 @@ const config = {
|
||||
new FriendlyErrorsWebpackPlugin(),
|
||||
new DefinePlugin({
|
||||
NODE_ENV: isProduction ? "'production'" : "'development'"
|
||||
})
|
||||
}),
|
||||
new VuetifyLoaderPlugin()
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,10 @@ const isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
const webpackConfig = merge(baseConfig, {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'node',
|
||||
devtool: isProduction ? false : 'source-map',
|
||||
entry: path.resolve(__dirname, '../client/entry.server.ts'),
|
||||
devtool: isProduction ? false : 'source-map',
|
||||
target: 'node',
|
||||
output: {
|
||||
filename: 'server-bundle.js',
|
||||
libraryTarget: 'commonjs2'
|
||||
},
|
||||
externals: nodeExternals({
|
||||
@ -21,7 +20,8 @@ const webpackConfig = merge(baseConfig, {
|
||||
/\.css$/,
|
||||
/\.sass$/,
|
||||
/\.scss$/,
|
||||
/\.svg$/
|
||||
/\.svg$/,
|
||||
/vuetify\/.*/
|
||||
]
|
||||
}),
|
||||
|
||||
|
@ -162,12 +162,15 @@ code.sourceCode {
|
||||
.sourceCode:not(:last-child) code.sourceCode {
|
||||
margin: 0 0 0.2rem;
|
||||
}
|
||||
a {
|
||||
a,
|
||||
/* So much selectors is required to overwrite vuetify styles */
|
||||
.v-application .app-content a {
|
||||
text-decoration-line: none;
|
||||
color: #0061c0;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration-line: underline;
|
||||
color: #005ebd;
|
||||
|
||||
&:hover {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
}
|
||||
.text-transform-none {
|
||||
text-transform: none !important;
|
||||
@ -212,23 +215,23 @@ blockquote {
|
||||
margin-right: 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
/* vuetify v-select component when opened almost overlays its title */
|
||||
.v-menu__content {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.svg-inline--fa {
|
||||
width: 1.25em !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
.v-toolbar__title {
|
||||
.v-toolbar__title {
|
||||
letter-spacing: inherit;
|
||||
}
|
||||
/* Some useless and obstructive div that vuetify add along with "v-menu" component */
|
||||
.v-menu--inline {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
/* Should be same padding on any screen cause it changes with toolbar and we fixed toolbar's height (see Toolbar.vue) height on each screen */
|
||||
.app-content {
|
||||
padding: 64px 0px 0px !important;
|
||||
padding: 100px 0px 36px !important;
|
||||
line-height: 150%;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,16 +1,10 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import Vuetify from 'vuetify'
|
||||
import vuetify from 'client/plugins/vuetify'
|
||||
import { sync } from 'vuex-router-sync'
|
||||
import ALink from 'client/components/ALink.vue'
|
||||
import confirmDialogMixin from 'client/mixins/confirmDialogMixin'
|
||||
import 'vuetify/dist/vuetify.css'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSquare } from '@fortawesome/free-regular-svg-icons'
|
||||
library.add(fas, faSquare)
|
||||
|
||||
import 'client/assets/code-highlight.css'
|
||||
|
||||
@ -18,27 +12,11 @@ import AppComponent from './App.vue'
|
||||
import { createRouter } from './router'
|
||||
import { createStore } from './store'
|
||||
|
||||
const icons = {}
|
||||
// TODO import and add only used icons for production
|
||||
Object.values({ ...fas, faSquare }).forEach(({ prefix, iconName }) => {
|
||||
icons[iconName] = {
|
||||
component: 'font-awesome-icon',
|
||||
props: {
|
||||
icon: [prefix, iconName]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function initVue () {
|
||||
Vue.use(VueRouter)
|
||||
Vue.use(Vuex)
|
||||
Vue.mixin(confirmDialogMixin)
|
||||
Vue.component('ALink', ALink)
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
Vue.use(Vuetify, {
|
||||
iconfont: 'faSvg',
|
||||
icons
|
||||
})
|
||||
}
|
||||
|
||||
function createApp () {
|
||||
@ -50,6 +28,7 @@ function createApp () {
|
||||
const app = new Vue({
|
||||
router,
|
||||
store,
|
||||
vuetify,
|
||||
render: h => h(AppComponent)
|
||||
})
|
||||
|
||||
|
@ -56,7 +56,7 @@ export default class AFooter extends Vue { }
|
||||
display: inline-block;
|
||||
}
|
||||
.footer {
|
||||
margin-right: -10px;
|
||||
margin: 0 -10px 0 0;
|
||||
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
|
@ -1,79 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-dialog
|
||||
lazy
|
||||
:value="value"
|
||||
@input="close"
|
||||
max-width="500px"
|
||||
<v-dialog
|
||||
:value="value"
|
||||
@input="close"
|
||||
max-width="500px"
|
||||
>
|
||||
|
||||
<template
|
||||
v-if="$slots.activator"
|
||||
v-slot:activator="{ on }"
|
||||
>
|
||||
<slot
|
||||
slot="activator"
|
||||
v-on="on"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<slot slot="activator" />
|
||||
|
||||
<v-card @keyup.esc.native="close" tabindex="0">
|
||||
<v-card-text>
|
||||
<v-form
|
||||
ref="form"
|
||||
lazy-validation
|
||||
v-model="isValid"
|
||||
@keydown.native.enter="submit"
|
||||
>
|
||||
<!-- v-if="value" - cause without it autofocus triggers on first modal open
|
||||
https://stackoverflow.com/questions/51472947/vuetifys-autofocus-works-only-on-first-modal-open -->
|
||||
<v-text-field
|
||||
v-if="value"
|
||||
class="mb-3"
|
||||
label="Category name"
|
||||
autofocus
|
||||
:rules="categoryValidationRules"
|
||||
v-model="categoryName"
|
||||
ref="categoryNameInput"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="groupNameInternal"
|
||||
label="Group"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
flat
|
||||
title="Cancel"
|
||||
color="primary"
|
||||
@click.native="close"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
title="Create"
|
||||
color="info"
|
||||
class="add-category-submit-btn"
|
||||
:disabled="!isValid || !categoryName"
|
||||
@click.native="submit"
|
||||
>
|
||||
Create
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
</v-dialog>
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="isValid"
|
||||
@keydown.native.enter="submit"
|
||||
>
|
||||
<!-- v-if="value" - cause without it autofocus triggers on first modal open
|
||||
https://stackoverflow.com/questions/51472947/vuetifys-autofocus-works-only-on-first-modal-open -->
|
||||
<v-text-field
|
||||
v-if="value"
|
||||
autofocus
|
||||
ref="categoryNameInput"
|
||||
class="mb-3"
|
||||
label="Category name"
|
||||
:rules="categoryValidationRules"
|
||||
v-model="categoryName"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="groupNameInternal"
|
||||
label="Group"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
text
|
||||
title="Cancel"
|
||||
color="primary"
|
||||
@click.native="close"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
title="Submit"
|
||||
color="info"
|
||||
class="add-category-submit-btn"
|
||||
:disabled="!isValid"
|
||||
@click.native="submit"
|
||||
>
|
||||
Submit
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
<!--
|
||||
`eager` prop is provided here because dialog doesn't get focus on first render without it
|
||||
which leads to inability of closing it with `esc` btn
|
||||
TODO fix, when issue is fixed in vuetify https://github.com/vuetifyjs/vuetify/issues/8220
|
||||
-->
|
||||
<ConfirmDialog
|
||||
v-if="isDuplicateConfirmShow"
|
||||
:value="isDuplicateConfirmShow"
|
||||
max-width="500px"
|
||||
attach="#app"
|
||||
eager
|
||||
ref="duplicateConfirm"
|
||||
max-width="500px"
|
||||
:value="isDuplicateConfirmShow"
|
||||
>
|
||||
This group already has categories with the same name:
|
||||
<router-link
|
||||
v-for="category in sameNameCategories"
|
||||
:key="category.id"
|
||||
:to="`/haskell/${getCategoryUrl(category)}`"
|
||||
target="_blank"
|
||||
>{{ category.title }}</router-link>
|
||||
<ul class="duplicate-categories-list">
|
||||
<li
|
||||
v-for="category in sameNameCategories"
|
||||
:key="category.id"
|
||||
>
|
||||
<router-link
|
||||
v-for="category in sameNameCategories"
|
||||
:key="category.id"
|
||||
:to="`/haskell/${getCategoryUrl(category)}`"
|
||||
target="_blank"
|
||||
>{{ category.title }}</router-link>>
|
||||
</li>
|
||||
</ul>
|
||||
</ConfirmDialog>
|
||||
</div>
|
||||
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -140,6 +155,7 @@ export default class AddCategoryDialog extends Vue {
|
||||
title: this.categoryName,
|
||||
group: this.groupNameInternal
|
||||
})
|
||||
this.close()
|
||||
this.$router.push(`haskell/${createdId}`)
|
||||
}
|
||||
|
||||
@ -148,9 +164,14 @@ export default class AddCategoryDialog extends Vue {
|
||||
this.isDuplicateConfirmShow = true
|
||||
this.$nextTick(() => {
|
||||
const duplicateConfirm = this.$refs.duplicateConfirm
|
||||
|
||||
duplicateConfirm.$once('canceled', () => {
|
||||
promise.resolve(false)
|
||||
this.isDuplicateConfirmShow = false
|
||||
|
||||
// when duplicateConfirm dialog closed it automatically sets focus on <body>
|
||||
// and we focus on name input for: so that user could change category name and so that he could push esc to close dialog
|
||||
this.$nextTick(() => this.$refs.categoryNameInput.focus())
|
||||
})
|
||||
duplicateConfirm.$once('confirmed', () => {
|
||||
promise.resolve(true)
|
||||
@ -164,5 +185,20 @@ export default class AddCategoryDialog extends Vue {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="postcss" scoped>
|
||||
.duplicate-categories-list {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
list-style-type: none;
|
||||
display: inline;
|
||||
|
||||
&:not(:last-child):after {
|
||||
content: ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -1,29 +1,34 @@
|
||||
<template>
|
||||
<v-dialog
|
||||
lazy
|
||||
:value="value"
|
||||
@input="close"
|
||||
@keyup.esc.native="close"
|
||||
max-width="500px"
|
||||
>
|
||||
<slot slot="activator" />
|
||||
<template
|
||||
v-if="$slots.activator"
|
||||
v-slot:activator="{ on }"
|
||||
>
|
||||
<slot
|
||||
slot="activator"
|
||||
v-on="on"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
lazy-validation
|
||||
ref="form"
|
||||
v-model="isValid"
|
||||
@keydown.native.prevent.enter="submit"
|
||||
ref="form"
|
||||
>
|
||||
<!-- v-if="value" - cause without it autofocus triggers on first modal open
|
||||
https://stackoverflow.com/questions/51472947/vuetifys-autofocus-works-only-on-first-modal-open -->
|
||||
<!-- reason for "v-if" - see AddCategoryDialog.vue template-->
|
||||
<v-text-field
|
||||
v-if="value"
|
||||
autofocus
|
||||
class="mb-2"
|
||||
label="Item name"
|
||||
:rules="itemValidationRules"
|
||||
:rules="nameValidationRules"
|
||||
v-model="name"
|
||||
/>
|
||||
<v-text-field
|
||||
@ -42,7 +47,7 @@
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
title="Cancel"
|
||||
color="primary"
|
||||
@click.native="close"
|
||||
@ -51,8 +56,8 @@
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="info"
|
||||
:disabled="!isValid"
|
||||
title="Create"
|
||||
:disabled="!isValid || !name"
|
||||
@click.native="submit"
|
||||
>
|
||||
Create
|
||||
@ -73,13 +78,15 @@ export default class AddItemDialog extends Vue {
|
||||
@Prop(String) categoryId!: string
|
||||
|
||||
name: string = ''
|
||||
itemValidationRules: Array<(x: string) => boolean | string> = [
|
||||
nameValidationRules: Array<(x: string) => boolean | string> = [
|
||||
(x: string) => !!x || 'Item name can not be empty'
|
||||
]
|
||||
hackage: string = ''
|
||||
link: string = ''
|
||||
isValid: boolean = false
|
||||
|
||||
// TODO create mixin or external dialog component which reset data on new open,
|
||||
// cause this code is duplicated in another dialog components (AddCategoryDialog)
|
||||
@Watch('value')
|
||||
onOpen () {
|
||||
this.name = ''
|
||||
|
@ -51,13 +51,13 @@
|
||||
</template>
|
||||
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
class="ma-0 mt-1 px-1"
|
||||
color="grey darken-2"
|
||||
title="Add new category"
|
||||
@click="openAddCategoryDialog(groupName)"
|
||||
>
|
||||
<v-icon size="14" class="mr-1" left>$vuetify.icons.plus</v-icon>
|
||||
<v-icon size="14" class="mr-1" left v-text="'$vuetify.icons.plus'"></v-icon>
|
||||
Add new category
|
||||
</v-btn>
|
||||
</div>
|
||||
@ -69,6 +69,9 @@
|
||||
v-model="isAddGroupDialogOpen"
|
||||
:groupName="addCategoryGroupName"
|
||||
/>
|
||||
<!-- <v-dialog v-model="isAddGroupDialogOpen">
|
||||
|
||||
</v-dialog> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -125,7 +128,8 @@ export default class Categories extends Vue {
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.categories {
|
||||
margin-top: 30px;
|
||||
/* Bellow we use negative margins for flex container and it creates unwanted horizontal scroll bar */
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.categories-flex-container {
|
||||
display: flex;
|
||||
@ -185,20 +189,23 @@ export default class Categories extends Vue {
|
||||
word-break: break-word;
|
||||
}
|
||||
.category-title {
|
||||
line-height: 1.2;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.category-title:not(:first-child) {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.category-title,
|
||||
.status-title {
|
||||
line-height: 1.2;
|
||||
}
|
||||
.status-title {
|
||||
font-size: 0.9rem;
|
||||
margin: 4px 0 0 8px;
|
||||
line-height: 1.7;
|
||||
margin: 6px 0 4px 8px;
|
||||
|
||||
+ .category-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
.group-title + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
@ -34,7 +34,7 @@
|
||||
/>
|
||||
</template>
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
class="ml-2"
|
||||
color="grey darken-2"
|
||||
title="Add new item"
|
||||
|
@ -16,8 +16,7 @@
|
||||
</a-link>
|
||||
</template>
|
||||
<span>RSS feed for all new items in this category</span>
|
||||
</v-tooltip>{{categoryTitle}}
|
||||
</h1>
|
||||
</v-tooltip>{{categoryTitle}}</h1>
|
||||
|
||||
<div class="category-header__second-row">
|
||||
<div class="category-group-title-wrap">
|
||||
@ -47,7 +46,7 @@
|
||||
<v-menu bottom left offset-y>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
icon
|
||||
title="Actions"
|
||||
class="category-actions-menu-btn"
|
||||
@ -164,15 +163,15 @@ export default class CategoryHeader extends Vue {
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.rss-link {
|
||||
/* For vertical aligning on one line with category title */
|
||||
font-size: 1px;
|
||||
margin-right: 6px;
|
||||
/* Space beetwen category rss ling title */
|
||||
.rss-link:after {
|
||||
content: " ";
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.rss-link-icon {
|
||||
height: calc(1.8rem - 5px) !important;
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
|
||||
&:hover {
|
||||
color: #000;
|
||||
@ -198,8 +197,10 @@ export default class CategoryHeader extends Vue {
|
||||
}
|
||||
|
||||
.category-actions-menu-btn {
|
||||
margin: 0;
|
||||
display: none;
|
||||
margin: 0;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.category-actions {
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
class="category-header-btn"
|
||||
:title="text"
|
||||
color="grey darken-2"
|
||||
:title="text"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
@ -25,9 +25,10 @@ export default class CategoryHeaderBtn extends Vue {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.category-header-btn {
|
||||
margin: 0;
|
||||
/* .v-btn selector added for more specificity to override vuetify styles without using !important*/
|
||||
.v-btn.category-header-btn {
|
||||
padding: 0 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,20 +1,30 @@
|
||||
<template>
|
||||
<v-dialog
|
||||
lazy
|
||||
:value="value"
|
||||
@input="close"
|
||||
max-width="500px"
|
||||
>
|
||||
<slot slot="activator" />
|
||||
|
||||
<template
|
||||
v-if="$slots.activator"
|
||||
v-slot:activator="{ on }"
|
||||
>
|
||||
<slot
|
||||
slot="activator"
|
||||
v-on="on"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
lazy-validation
|
||||
v-model="isValid"
|
||||
@keydown.native.prevent.ctrl.enter="updateCategoryInfo"
|
||||
>
|
||||
<!-- reason for "v-if" - see AddCategoryDialog.vue template-->
|
||||
<v-text-field
|
||||
v-if="value"
|
||||
autofocus
|
||||
class="mb-2"
|
||||
label="Title"
|
||||
@ -22,47 +32,53 @@
|
||||
v-model="title"
|
||||
/>
|
||||
<v-text-field
|
||||
autofocus
|
||||
class="mb-2"
|
||||
label="Group"
|
||||
:rules="inputValidationRules"
|
||||
v-model="group"
|
||||
/>
|
||||
<v-select
|
||||
:items="categoryStatuses"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
:menu-props="{ offsetY: true, closeOnClick: true }"
|
||||
:items="categoryStatuses"
|
||||
v-model="categoryStatus"
|
||||
label="Status"
|
||||
class="mb-2"
|
||||
/>
|
||||
<v-checkbox
|
||||
:inputValue="checkboxSections"
|
||||
@click.native.capture.prevent.stop="updateSectionEnabling('ItemProsConsSection', 'Pros/Cons')"
|
||||
hide-details
|
||||
color="info"
|
||||
class="category-info-edit__checkbox"
|
||||
label="Pros/cons section"
|
||||
value="ItemProsConsSection"
|
||||
class="mt-0 hide-v-messages"
|
||||
:inputValue="checkboxSections"
|
||||
@click.native.capture.prevent.stop="updateSectionEnabling('ItemProsConsSection', 'Pros/Cons')"
|
||||
/>
|
||||
<v-checkbox
|
||||
:inputValue="checkboxSections"
|
||||
@click.native.capture.prevent.stop="updateSectionEnabling('ItemEcosystemSection', 'Ecosystem')"
|
||||
hide-details
|
||||
color="info"
|
||||
class="category-info-edit__checkbox"
|
||||
label="Ecosystem section"
|
||||
value="ItemEcosystemSection"
|
||||
class="mt-0 hide-v-messages"
|
||||
:inputValue="checkboxSections"
|
||||
@click.native.capture.prevent.stop="updateSectionEnabling('ItemEcosystemSection', 'Ecosystem')"
|
||||
/>
|
||||
<v-checkbox
|
||||
:inputValue="checkboxSections"
|
||||
@click.native.capture.prevent.stop="updateSectionEnabling('ItemNotesSection', 'Notes')"
|
||||
hide-details
|
||||
color="info"
|
||||
class="category-info-edit__checkbox"
|
||||
label="Notes section"
|
||||
value="ItemNotesSection"
|
||||
class="mt-0 hide-v-messages"
|
||||
:inputValue="checkboxSections"
|
||||
@click.native.capture.prevent.stop="updateSectionEnabling('ItemNotesSection', 'Notes')"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
title="Cancel"
|
||||
color="primary"
|
||||
@click.native="close"
|
||||
@ -177,9 +193,17 @@ export default class CategoryInfoEdit extends Vue {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
>>> .hide-v-messages .v-messages {
|
||||
display: none;
|
||||
<style lang="postcss" scoped>
|
||||
.category-info-edit__checkbox >>> {
|
||||
.v-input--selection-controls__input {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
/* We using fontawesome icons which turned out to be bigger than vuetify default icons
|
||||
and checkbox icon gets some strange offset if overflow is visible */
|
||||
svg {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -67,7 +67,7 @@
|
||||
<v-btn
|
||||
small
|
||||
dark
|
||||
round
|
||||
rounded
|
||||
title="Expand"
|
||||
class="mx-0"
|
||||
@click="expandNotes"
|
||||
@ -77,7 +77,7 @@
|
||||
<v-btn
|
||||
small
|
||||
dark
|
||||
round
|
||||
rounded
|
||||
title="Collapse"
|
||||
class="mx-0"
|
||||
@click="collapseNotes"
|
||||
@ -211,14 +211,15 @@ export default class CategoryItem extends Vue {
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.category-item-body {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
position: relative;
|
||||
background: #eeeeee;
|
||||
margin: 0 0 40px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.category-item-body {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.category-item-traits {
|
||||
|
@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<v-btn
|
||||
:icon="!!icon && !showTitle"
|
||||
flat
|
||||
class="ma-0 font-weight-bold"
|
||||
color="grey darken-2"
|
||||
:style="style"
|
||||
:title="title"
|
||||
v-bind="$attrs"
|
||||
class="font-weight-bold"
|
||||
v-bind="{
|
||||
color: 'grey darken-2',
|
||||
icon: !!icon && !showTitle,
|
||||
style,
|
||||
title,
|
||||
...$attrs
|
||||
}"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<v-icon
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="category-item-section">
|
||||
<section class="category-item-section">
|
||||
<h3 class="category-item-section__title title font-weight-bold mb-1 mt-0">
|
||||
{{ title }}
|
||||
<category-item-btn
|
||||
@ -22,7 +22,7 @@
|
||||
@cancel="toggleEdit"
|
||||
@save="save"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -80,6 +80,9 @@ export default class CategoryItemSection extends Vue {
|
||||
|
||||
|
||||
<style scoped>
|
||||
.category-item-section {
|
||||
max-width: 100%;
|
||||
}
|
||||
.category-item-section__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -1,15 +1,9 @@
|
||||
<template>
|
||||
<v-expansion-panel class="category-item-toolbar">
|
||||
<v-expansion-panel-content
|
||||
hide-actions
|
||||
:value="isEditItemInfoMenuOpen"
|
||||
>
|
||||
<div class="category-item-toolbar">
|
||||
<div class="category-item-toolbar__header">
|
||||
<v-toolbar
|
||||
flat
|
||||
slot="header"
|
||||
color="#dedede"
|
||||
class="elevation-2"
|
||||
@click.stop=""
|
||||
>
|
||||
<v-toolbar-title class="text-h2">
|
||||
<span class="category-item-toolbar-title">
|
||||
@ -26,7 +20,6 @@
|
||||
openInNewTab
|
||||
>{{ itemName }}</a-link>
|
||||
<span class="category-item-name" v-else>{{ itemName }}</span>
|
||||
|
||||
<div class="category-item-badges">
|
||||
<a
|
||||
v-if="this.itemHackage"
|
||||
@ -36,8 +29,7 @@
|
||||
>
|
||||
<v-icon
|
||||
color="#fff"
|
||||
class="mr-1"
|
||||
size="12"
|
||||
size="10"
|
||||
>$vuetify.icons.link</v-icon>hackage</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,18 +41,21 @@
|
||||
<v-toolbar-items>
|
||||
<div class="category-item-toolbar-btns">
|
||||
<category-item-btn
|
||||
size="40px"
|
||||
iconSize="18"
|
||||
title="Move item up"
|
||||
icon="arrow-up"
|
||||
@click="moveItem('up')"
|
||||
/>
|
||||
<category-item-btn
|
||||
size="40px"
|
||||
iconSize="18"
|
||||
title="Move item down"
|
||||
icon="arrow-down"
|
||||
@click="moveItem('down')"
|
||||
/>
|
||||
<category-item-btn
|
||||
size="40px"
|
||||
iconSize="18"
|
||||
title="Edit item info"
|
||||
icon="cog"
|
||||
@ -75,6 +70,7 @@
|
||||
</category-item-btn>
|
||||
|
||||
<category-item-btn
|
||||
size="40px"
|
||||
iconSize="18"
|
||||
title="Delete item"
|
||||
icon="trash-alt"
|
||||
@ -85,8 +81,8 @@
|
||||
<v-menu bottom left offset-y>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn
|
||||
flat
|
||||
icon
|
||||
small
|
||||
title="Actions"
|
||||
class="category-toolbar-mobile-menu-btn"
|
||||
v-on="on"
|
||||
@ -98,27 +94,33 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<v-list class="category-item-toolbar-mobile-menu-list">
|
||||
<v-list-tile>
|
||||
<v-list class="category-item-toolbar__mobile-menu-list">
|
||||
<v-list-item>
|
||||
<category-item-btn
|
||||
block
|
||||
text
|
||||
showTitle
|
||||
iconSize="18"
|
||||
title="Move item up"
|
||||
icon="arrow-up"
|
||||
@click="moveItem('up')"
|
||||
/>
|
||||
</v-list-tile>
|
||||
<v-list-tile>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<category-item-btn
|
||||
block
|
||||
text
|
||||
showTitle
|
||||
iconSize="18"
|
||||
title="Move item down"
|
||||
icon="arrow-down"
|
||||
@click="moveItem('down')"
|
||||
/>
|
||||
</v-list-tile>
|
||||
<v-list-tile>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<category-item-btn
|
||||
block
|
||||
text
|
||||
showTitle
|
||||
iconSize="18"
|
||||
title="Edit item info"
|
||||
@ -132,21 +134,29 @@
|
||||
size="8"
|
||||
>$vuetify.icons.circle</v-icon>
|
||||
</category-item-btn>
|
||||
</v-list-tile>
|
||||
<v-list-tile>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<category-item-btn
|
||||
block
|
||||
text
|
||||
showTitle
|
||||
iconSize="18"
|
||||
title="Delete item"
|
||||
icon="trash-alt"
|
||||
@click="deleteItem"
|
||||
/>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-toolbar-items>
|
||||
</v-toolbar>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="category-item-toolbar__expanding-content"
|
||||
:class="{ 'category-item-toolbar__expanding-content_expanded': isEditItemInfoMenuOpen }"
|
||||
:aria-hidden="!isEditItemInfoMenuOpen"
|
||||
>
|
||||
<v-layout column class="pa-3">
|
||||
<v-flex>
|
||||
<v-form @keydown.native.enter.ctrl="updateItemInfo">
|
||||
@ -166,6 +176,7 @@
|
||||
</v-flex>
|
||||
<v-flex align-self-end>
|
||||
<v-btn
|
||||
class="mr-1"
|
||||
title="Cancel"
|
||||
@click="resetAndToggleEditItemInfoMenu"
|
||||
>
|
||||
@ -173,7 +184,6 @@
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="info"
|
||||
class="mr-0"
|
||||
title="Save"
|
||||
:disabled="!isInfoSaveEnabled"
|
||||
@click="updateItemInfo"
|
||||
@ -182,8 +192,8 @@
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-expansion-panel-content>
|
||||
</v-expansion-panel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -286,53 +296,40 @@ export default class CategoryItemToolbar extends Vue {
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.category-item-toolbar {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
/* TODO move expanding panel to external component */
|
||||
.category-item-toolbar__expanding-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
background: #d6d6d6;
|
||||
transition: max-height 0.2s ease-in-out;
|
||||
}
|
||||
.category-item-toolbar__expanding-content_expanded {
|
||||
max-height: 300px;
|
||||
}
|
||||
.category-item-toolbar__header >>> {
|
||||
.v-toolbar__content {
|
||||
justify-content: space-between;
|
||||
/* Vuetify sets inline height style */
|
||||
height: auto !important;
|
||||
align-items: flex-start;
|
||||
padding: 8px 16px;
|
||||
|
||||
>>> {
|
||||
.v-toolbar__content {
|
||||
justify-content: space-between;
|
||||
height: auto !important;
|
||||
min-height: 56px;
|
||||
|
||||
.v-toolbar__title {
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
overflow: visible;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.v-toolbar__items {
|
||||
height: 100%;
|
||||
margin-top: 10px;
|
||||
align-self: baseline;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 959px) {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.v-expansion-panel__header {
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
cursor: unset;
|
||||
.v-toolbar__items {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.v-expansion-panel__body {
|
||||
background: rgb(222, 222, 222);
|
||||
@media screen and (max-width: 959px) {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.category-item-toolbar-title {
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
line-height: 1;
|
||||
}
|
||||
.category-item-anchor {
|
||||
color: rgb(151, 151, 151);
|
||||
@ -343,22 +340,35 @@ export default class CategoryItemToolbar extends Vue {
|
||||
.category-item-name {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
|
||||
/* A gap beetwen category item name and badges */
|
||||
@media (min-width: 768px) {
|
||||
&:after {
|
||||
content: " ";
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
.category-item-badges {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
|
||||
> * {
|
||||
vertical-align: middle;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.1px 5px;
|
||||
line-height: 1;
|
||||
border-radius: 5px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.hackage-link {
|
||||
background: #5e5184;
|
||||
color: #fff;
|
||||
|
||||
svg {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.unsaved-changes-icon {
|
||||
@ -371,42 +381,59 @@ export default class CategoryItemToolbar extends Vue {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
> * {
|
||||
width: 1.6rem !important;
|
||||
height: 1.6rem !important;
|
||||
}
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.category-toolbar-mobile-menu-btn {
|
||||
display: none;
|
||||
/* Somewhy vuetify sets important "height: 100%"" for direct child buttons of toolbar */
|
||||
height: 1.6rem !important;
|
||||
width: 1.6rem !important;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-item-toolbar-mobile-menu-list {
|
||||
>>> .v-list__tile {
|
||||
.category-item-toolbar__mobile-menu-list {
|
||||
>>> .v-list-item {
|
||||
height: 36px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
padding: 0;
|
||||
|
||||
>>> button {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
button {
|
||||
height: 100% !important;
|
||||
padding: 0 6px;
|
||||
|
||||
.v-btn__content {
|
||||
justify-content: flex-start;
|
||||
.v-btn__content {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.category-toolbar-mobile-menu-btn {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
.category-item-toolbar-btns {
|
||||
display: none;
|
||||
}
|
||||
.category-item-badges {
|
||||
display: block;
|
||||
margin: 5px 0 0 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 6px 0 0 0;
|
||||
}
|
||||
.unsaved-changes-icon {
|
||||
right: unset;
|
||||
left: 13px;
|
||||
}
|
||||
>>> .v-toolbar__items {
|
||||
margin-left: 5px;
|
||||
align-self: baseline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -13,63 +13,59 @@
|
||||
class="position-relative category-item-trait"
|
||||
>
|
||||
|
||||
<div
|
||||
v-html="trait.content.html"
|
||||
v-if="!trait.isEdit"
|
||||
/>
|
||||
<template v-if="!trait.isEdit">
|
||||
<div v-html="trait.content.html" />
|
||||
|
||||
<v-menu
|
||||
v-if="!trait.isEdit"
|
||||
lazy
|
||||
offset-y
|
||||
nudge-bottom="2"
|
||||
class="category-item-edit-trait-menu"
|
||||
>
|
||||
<category-item-btn
|
||||
slot="activator"
|
||||
size="22px"
|
||||
title="Actions"
|
||||
iconSize="14"
|
||||
icon="ellipsis-v"
|
||||
/>
|
||||
<v-list dense>
|
||||
<v-list-tile
|
||||
tag="div"
|
||||
@click="moveTrait(trait, 'up')"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
Move up
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
<v-menu bottom left offset-y>
|
||||
<template v-slot:activator="{ on }">
|
||||
<category-item-btn
|
||||
class="trait-actions-menu-btn"
|
||||
size="22px"
|
||||
title="Actions"
|
||||
iconSize="14"
|
||||
icon="ellipsis-v"
|
||||
v-on='on'
|
||||
/>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item
|
||||
tag="div"
|
||||
@click="moveTrait(trait, 'up')"
|
||||
>
|
||||
<v-list-item-title>
|
||||
Move up
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-tile
|
||||
tag="div"
|
||||
@click="moveTrait(trait, 'down')"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
Move down
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
<v-list-item
|
||||
tag="div"
|
||||
@click="moveTrait(trait, 'down')"
|
||||
>
|
||||
<v-list-item-title>
|
||||
Move down
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-tile
|
||||
tag="div"
|
||||
@click="trait.isEdit = true"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
Edit
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
<v-list-item
|
||||
tag="div"
|
||||
@click="trait.isEdit = true"
|
||||
>
|
||||
<v-list-item-title>
|
||||
Edit
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-tile
|
||||
tag="div"
|
||||
@click="deleteTrait(trait)"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
Delete
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-list-item
|
||||
tag="div"
|
||||
@click="deleteTrait(trait)"
|
||||
>
|
||||
<v-list-item-title>
|
||||
Delete
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<markdown-editor
|
||||
v-if="trait.isEdit"
|
||||
@ -208,12 +204,12 @@ export default class CategoryItemTraits extends Vue {
|
||||
.category-item-trait {
|
||||
padding-right: 24px;
|
||||
}
|
||||
.category-item-edit-trait-menu {
|
||||
.trait-actions-menu-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
>>> .v-list__tile {
|
||||
>>> .v-list-item {
|
||||
padding: 0 8px;
|
||||
}
|
||||
</style>
|
@ -1,27 +1,38 @@
|
||||
<!-- Universal confirmation dialog, just pass text and function in Props -->
|
||||
<template>
|
||||
<v-dialog
|
||||
lazy
|
||||
:value="value"
|
||||
:attach="attach"
|
||||
max-width="500px"
|
||||
@input="close"
|
||||
v-bind="$attrs"
|
||||
@input="cancel"
|
||||
>
|
||||
<slot slot="activator" />
|
||||
|
||||
<template
|
||||
v-if="$slots.activator"
|
||||
v-slot:activator="{ on }"
|
||||
>
|
||||
<slot
|
||||
slot="activator"
|
||||
v-on="on"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-card>
|
||||
<v-card-text v-if="$slots.default">
|
||||
<slot />
|
||||
</v-card-text>
|
||||
<v-card-text v-else>
|
||||
{{ fullText || `Are you sure you want to ${text}?` }}
|
||||
<v-card-text class="confirm-dialog__text">
|
||||
<slot v-if="$slots.default"/>
|
||||
|
||||
<template v-else>
|
||||
{{ fullText || `Are you sure you want to ${text}?` }}
|
||||
</template>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider />
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-bind="{
|
||||
flat: true,
|
||||
text: true,
|
||||
color:'primary',
|
||||
title: cancelBtnText,
|
||||
...cancelBtnProps
|
||||
@ -50,7 +61,9 @@ import Vue from 'vue'
|
||||
import Component from 'vue-class-component'
|
||||
import { Prop } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
@Component({
|
||||
inheritAttrs: false
|
||||
})
|
||||
export default class ConfirmDialog extends Vue {
|
||||
@Prop(String) text!: string
|
||||
@Prop(String) fullText!: string
|
||||
@ -63,7 +76,6 @@ export default class ConfirmDialog extends Vue {
|
||||
|
||||
close () {
|
||||
this.$emit('input', false)
|
||||
this.$emit('canceled')
|
||||
}
|
||||
confirm () {
|
||||
this.$emit('confirmed')
|
||||
@ -75,3 +87,12 @@ export default class ConfirmDialog extends Vue {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-card__text.confirm-dialog__text {
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
<template>
|
||||
<v-dialog
|
||||
lazy
|
||||
persistent
|
||||
:value="value"
|
||||
>
|
||||
<slot slot="activator" />
|
||||
|
||||
<template
|
||||
v-if="$slots.activator"
|
||||
v-slot:activator="{ on }"
|
||||
>
|
||||
<slot
|
||||
slot="activator"
|
||||
v-on="on"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="conflict-box">
|
||||
<div class="conflict-item">
|
||||
@ -135,6 +143,10 @@ export default class ConflictDialog extends Vue {
|
||||
}
|
||||
.v-card__text {
|
||||
word-break: break-word;
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
letter-spacing: normal;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,33 +16,31 @@
|
||||
|
||||
<v-toolbar
|
||||
flat
|
||||
height="30"
|
||||
height="auto"
|
||||
color="#e5e5e5"
|
||||
class="pa-2 markdown-editor__bottom-toolbar"
|
||||
class="markdown-editor__bottom-toolbar"
|
||||
v-if="bottomToolbar"
|
||||
v-show="editor"
|
||||
>
|
||||
<span class="markdown-editor-save-tip">
|
||||
{{ saveTip }}
|
||||
</span>
|
||||
<v-toolbar-items>
|
||||
<v-btn
|
||||
small
|
||||
title="Save"
|
||||
class="mr-2 text-transform-none"
|
||||
@click="save"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
<v-btn
|
||||
small
|
||||
class="mr-2"
|
||||
title="Cancel"
|
||||
class="text-transform-none"
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="info"
|
||||
title="Save"
|
||||
@click="save"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
</v-toolbar-items>
|
||||
<span class="markdown-editor-save-tip ml-1">
|
||||
{{ saveTip }}
|
||||
</span>
|
||||
</v-toolbar>
|
||||
</div>
|
||||
</template>
|
||||
@ -266,14 +264,25 @@ export default class MarkdownEditor extends Vue {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.markdown-editor__bottom-toolbar {
|
||||
border: 1px solid #bbb !important;
|
||||
border-top: none !important;
|
||||
}
|
||||
>>> .v-toolbar__content {
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-editor-save-tip {
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
>>> {
|
||||
.v-toolbar__content {
|
||||
padding: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.v-toolbar__items > .v-btn {
|
||||
height: 26px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-editor-save-tip {
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
margin-right: 5px;
|
||||
|
||||
@media screen and (max-width: 425px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -2,6 +2,7 @@
|
||||
<v-text-field
|
||||
dark
|
||||
solo
|
||||
hide-details
|
||||
class="toolbar-search"
|
||||
label="Search in all pages"
|
||||
:value="searchInput"
|
||||
@ -36,10 +37,5 @@ export default class SearchField extends Vue {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toolbar-search >>> div {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.toolbar-search >>> label {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,17 +1,21 @@
|
||||
<template>
|
||||
<v-toolbar dark app>
|
||||
<v-app-bar
|
||||
dark
|
||||
app
|
||||
color="#232323"
|
||||
>
|
||||
<v-toolbar-title>
|
||||
<logo
|
||||
:class="{ 'mobile-hidden': !isSearchFieldHidden }"
|
||||
/>
|
||||
<logo :class="{ 'mobile-hidden': !isSearchFieldHidden }"/>
|
||||
</v-toolbar-title>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<search-field
|
||||
:class="{ 'mobile-hidden': isSearchFieldHidden }"
|
||||
ref="searchField"
|
||||
/>
|
||||
<v-btn
|
||||
flat
|
||||
text
|
||||
icon
|
||||
title="Search"
|
||||
color="#fff"
|
||||
@ -20,7 +24,7 @@
|
||||
>
|
||||
<v-icon size="20">$vuetify.icons.search</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -28,7 +32,6 @@ import Vue from 'vue'
|
||||
import SearchField from 'client/components/SearchField.vue'
|
||||
import Logo from 'client/components/Logo.vue'
|
||||
import Component from 'vue-class-component'
|
||||
import axios from 'axios'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@ -52,9 +55,6 @@ export default class Toolbar extends Vue {
|
||||
>>> .v-toolbar__content {
|
||||
height: 64px !important;
|
||||
}
|
||||
.mobile-hidden {
|
||||
display: block;
|
||||
}
|
||||
.mobile-displayed {
|
||||
display: none;
|
||||
}
|
||||
@ -63,7 +63,7 @@ export default class Toolbar extends Vue {
|
||||
display: none;
|
||||
}
|
||||
.mobile-displayed {
|
||||
display: block;
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -62,6 +62,9 @@ function getComponentAndItsChildren (component, result?) {
|
||||
if (!result) {
|
||||
result = []
|
||||
}
|
||||
if (!component.options) {
|
||||
return result
|
||||
}
|
||||
if (!result.includes(component)) {
|
||||
result.push(component)
|
||||
}
|
||||
|
@ -33,11 +33,11 @@ export default class ConfirmDialogMixin extends Vue {
|
||||
cancelBtnText,
|
||||
confirmBtnProps,
|
||||
cancelBtnProps
|
||||
}
|
||||
},
|
||||
parent: this
|
||||
})
|
||||
instance.$mount()
|
||||
const deferredPromise = new DeferredPromise()
|
||||
this.$el.appendChild(instance.$el)
|
||||
instance.$on('confirmed', () => {
|
||||
instance.$destroy()
|
||||
deferredPromise.resolve(true)
|
||||
@ -46,6 +46,8 @@ export default class ConfirmDialogMixin extends Vue {
|
||||
instance.$destroy()
|
||||
deferredPromise.resolve(false)
|
||||
})
|
||||
this.$el.appendChild(instance.$el)
|
||||
|
||||
return deferredPromise
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,16 @@ export default class ConflictDialogMixin extends Vue {
|
||||
serverModified,
|
||||
modified,
|
||||
merged
|
||||
}
|
||||
},
|
||||
parent: this
|
||||
})
|
||||
instance.$mount()
|
||||
const deferredPromise = new DeferredPromise()
|
||||
this.$el.appendChild(instance.$el)
|
||||
instance.$on('save', (newVal) => {
|
||||
instance.$destroy()
|
||||
deferredPromise.resolve(newVal)
|
||||
})
|
||||
this.$el.appendChild(instance.$el)
|
||||
|
||||
return deferredPromise
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="text-md-center display-1"
|
||||
class="text-center display-1"
|
||||
>
|
||||
Nothing found
|
||||
</div>
|
||||
|
34
front/client/plugins/vuetify.ts
Normal file
34
front/client/plugins/vuetify.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import Vue from 'vue'
|
||||
import Vuetify from 'vuetify/lib'
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSquare, faCheckSquare } from '@fortawesome/free-regular-svg-icons'
|
||||
library.add(fas, faSquare, faCheckSquare)
|
||||
|
||||
const icons = {}
|
||||
const addIcon = ({ prefix, iconName }, iconUsageName = iconName) => {
|
||||
icons[iconUsageName] = {
|
||||
component: 'font-awesome-icon',
|
||||
props: {
|
||||
icon: [prefix, iconName]
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO import and add only used icons for production
|
||||
Object.values({ ...fas, faSquare, faCheckSquare }).forEach(x => addIcon(x))
|
||||
|
||||
// This is for vuetify v-checkbox, v-select components, which uses by default icons with such names
|
||||
addIcon(faCheckSquare, 'checkboxOn')
|
||||
addIcon(faSquare, 'checkboxOff')
|
||||
addIcon(fas.faCaretDown, 'dropdown')
|
||||
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
values: icons
|
||||
}
|
||||
})
|
3602
front/package-lock.json
generated
3602
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -28,67 +28,73 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.17",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.8.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.8.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.20",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.10.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.10.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.6",
|
||||
"axios": "^0.18.1",
|
||||
"easymde": "^2.6.0",
|
||||
"axios": "^0.19.0",
|
||||
"easymde": "^2.7.0",
|
||||
"koa": "^2.7.0",
|
||||
"koa-bodyparser": "^4.2.1",
|
||||
"koa-mount": "^4.0.0",
|
||||
"koa-proxy": "^0.9.0",
|
||||
"koa-static": "^5.0.0",
|
||||
"lodash": "^4.17.13",
|
||||
"lodash": "^4.17.15",
|
||||
"moment": "^2.24.0",
|
||||
"normalize-url": "^4.3.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-class-component": "^7.0.2",
|
||||
"vue-mixin-decorator": "^1.1.1",
|
||||
"vue-property-decorator": "^8.1.0",
|
||||
"vue-router": "^3.0.4",
|
||||
"vue-class-component": "^7.1.0",
|
||||
"vue-mixin-decorator": "^1.2.0",
|
||||
"vue-property-decorator": "^8.2.1",
|
||||
"vue-router": "^3.0.7",
|
||||
"vue-server-renderer": "^2.6.10",
|
||||
"vuetify": "^1.5.11",
|
||||
"vuex": "^3.1.0",
|
||||
"vuetify": "^2.0.3",
|
||||
"vuex": "^3.1.1",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.3",
|
||||
"fibers": "^4.0.1",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-transform-runtime": "^7.4.3",
|
||||
"@babel/preset-env": "^7.4.3",
|
||||
"@babel/plugin-transform-runtime": "^7.5.5",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@vue/preload-webpack-plugin": "^1.1.0",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"concurrently": "^4.1.0",
|
||||
"copyfiles": "^2.1.0",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"babel-loader": "^8.0.6",
|
||||
"concurrently": "^4.1.1",
|
||||
"copyfiles": "^2.1.1",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^2.1.1",
|
||||
"file-loader": "^3.0.1",
|
||||
"fork-ts-checker-webpack-plugin": "^1.0.2",
|
||||
"css-loader": "^3.1.0",
|
||||
"deepmerge": "^4.0.0",
|
||||
"file-loader": "^4.1.0",
|
||||
"fork-ts-checker-webpack-plugin": "^1.4.3",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"koa-webpack": "^5.2.2",
|
||||
"koa-webpack": "^5.2.4",
|
||||
"memory-fs": "^0.4.1",
|
||||
"nodemon": "^1.18.11",
|
||||
"nodemon": "^1.19.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.6.0",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"precss": "^4.0.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"testcafe": "^1.1.2",
|
||||
"sass": "^1.22.9",
|
||||
"sass-loader": "^7.1.0",
|
||||
"testcafe": "^1.3.3",
|
||||
"testcafe-vue-selectors": "^3.1.0",
|
||||
"ts-loader": "^5.3.3",
|
||||
"ts-node": "^8.0.3",
|
||||
"tslint": "^5.15.0",
|
||||
"typescript": "^3.4.3",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue-loader": "^15.7.0",
|
||||
"ts-loader": "^6.0.4",
|
||||
"ts-node": "^8.3.0",
|
||||
"tslint": "^5.18.0",
|
||||
"typescript": "^3.5.3",
|
||||
"url-loader": "^2.1.0",
|
||||
"vue-loader": "^15.7.1",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.30.0",
|
||||
"webpack-cli": "^3.3.0",
|
||||
"vuetify-loader": "^1.3.0",
|
||||
"webpack": "^4.38.0",
|
||||
"webpack-cli": "^3.3.6",
|
||||
"webpack-merge": "^4.2.1",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"webpackbar": "^3.1.5"
|
||||
"webpackbar": "^3.2.0"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user