1
1
mirror of https://github.com/aelve/guide.git synced 2024-11-22 11:33:34 +03:00

Adaptation for mobile screens (#340)

* Consistent headings and paragraph styles for entire app

* Search adopted for mobile

* Category item adapted for mobile

* Consistent category header icon sizes and paddings, relatively rest of category page

* Hide logo when search input displayed on mobile

* removed excess css rule

* font size and line height changed

* Badge adjusted for new font size

* Lists margins added

* Footer padding and margins adapted for mobile

* Markdown editor correct focus on open

* Markdown editor autofocus now optionabe

* Typo fix

* Markdown editor component more props and styling

* Markdown editor border stylings

* Conflict dialog refactor and adapting for mobiles

* Typo fix

* Categories page restyled and adapted for mobiles

* Categories page group titles allow break words

* Removed excess comparison
This commit is contained in:
avele 2019-07-24 19:33:02 +04:00 committed by GitHub
parent bff3cac46e
commit 613ecf8c9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 732 additions and 281 deletions

View File

@ -43,21 +43,93 @@ export default class RootComponent extends Vue {
}
</script>
<style>
<style lang="postcss">
*,
*:before,
*:after {
box-sizing: border-box;
}
html {
/*
Formula is: calc(
[min-font-size] +
([max-font-size] - [min-font-size] * (100vw - [min-vw]) / ([max-vw] - [min-vw])
);
*/
font-size: calc(16px + (18 - 16) * (100vw - 320px) / (1920 - 320));
}
:root {
--h1-font-size: 1.8rem;
--h2-font-size: 1.4rem;
--h3-font-size: 1.17rem;
--h4-font-size: 1rem;
--h5-font-size: 0.83rem;
--h6-font-size: 0.75rem;
}
h1 {
font-size: var(--h1-font-size);
margin: 1.95rem 0 0.6rem;
}
h2 {
font-size: var(--h2-font-size);
margin: 1.65rem 0 0.45rem;
}
h3 {
font-size: var(--h3-font-size);
margin: 1.35rem 0 0.35rem;
}
h4 {
font-size: var(--h4-font-size);
margin: 1rem 0 0.25rem;
}
h5 {
font-size: var(--h5-font-size);
margin: 0.8rem 0 0.2rem;
}
h6 {
font-size: var(--h6-font-size);
margin: 0.5rem 0 0;
}
.text-h1 {
font-size: var(--h1-font-size);
}
.text-h2 {
font-size: var(--h2-font-size);
}
.text-h3 {
font-size: var(--h3-font-size);
}
.text-h4 {
font-size: var(--h4-font-size);
}
.text-h5 {
font-size: var(--h5-font-size);
}
.text-h6 {
font-size: var(--h6-font-size);
}
p {
margin-bottom: 10px;
margin: 0.2rem 0 1.2rem 0;
}
ul,
ol {
margin: 0.8rem 0;
}
ul > li:not(:last-child),
ol > li:not(:last-child) {
margin-bottom: 5px;
}
li p {
margin-bottom: 0;
}
ul li:not(:last-child) {
margin-bottom: 2px;
margin-top: 0;
}
code {
color: inherit;
text-decoration: inherit;
@ -88,7 +160,7 @@ code.sourceCode {
padding: 8px;
}
.sourceCode:not(:last-child) code.sourceCode {
margin: 0 0 5px;
margin: 0 0 0.2rem;
}
a {
text-decoration-line: none;
@ -157,5 +229,6 @@ blockquote {
/* 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;
line-height: 150%;
}
</style>

View File

@ -1,5 +1,6 @@
<template>
<v-footer
class="pa-2"
height="auto"
color="lighten-1"
>
@ -8,8 +9,9 @@
align-center
row
wrap
class="footer"
>
<span class="mr-3">
<span>
made by
<a-link
openInNewTab
@ -18,7 +20,7 @@
Aelve
</a-link>
</span>
<span class="mr-3">
<span>
<a-link
openInNewTab
url="https://github.com/aelve/guide"
@ -49,8 +51,15 @@ import Component from 'vue-class-component'
export default class AFooter extends Vue { }
</script>
<style scoped>
<style lang="postcss" scoped>
.footer-link {
display: inline-block;
}
.footer {
margin-right: -10px;
> * {
margin-right: 10px;
}
}
</style>

View File

@ -1,95 +1,75 @@
<template>
<v-container grid-list-md>
<v-layout
row
wrap
justify-space-between
>
<v-flex
class="mr-3 mt-3"
column
xs12
sm5
md3
lg3
xl1
<div class="categories">
<div class="categories-flex-container">
<div
class="categories-column"
v-for="(groupCategories, groupName, index) in groups"
:key="index"
>
<div class="category-group">
<h4 class="mb-2 display-1 font-weight-black category-group-name">
<h2 class="mt-0 mb-2 group-title">
{{ groupName }}
</h4>
</h2>
<router-link
class="category-title"
class="category-title ml-2"
v-for="category in groupCategories[CategoryStatus.finished]"
:key="category.id"
:to="`/haskell/${getCategoryUrl(category)}`"
>
<h6
class="ml-2 subheading font-weight-bold"
>
{{ category.title }}
</h6>
{{ category.title }}
</router-link>
<h6
class="ml-2 mb-1 body-2 font-weight-bold"
v-if="groupCategories[CategoryStatus.inProgress]"
>
In progress
</h6>
<router-link
class="category-title ml-3"
v-for="category in groupCategories[CategoryStatus.inProgress]"
:key="category.id"
:to="`/haskell/${getCategoryUrl(category)}`"
>
<h6
class="ml-2 body-1 font-weight-bold"
<template v-if="groupCategories[CategoryStatus.inProgress]">
<h3 class="status-title">
In progress
</h3>
<router-link
class="category-title ml-3"
v-for="category in groupCategories[CategoryStatus.inProgress]"
:key="category.id"
:to="`/haskell/${getCategoryUrl(category)}`"
>
{{ category.title }}
</h6>
</router-link>
</router-link>
</template>
<h6
class="ml-2 mb-1 body-2 font-weight-bold"
v-if="groupCategories[CategoryStatus.toBeWritten]"
>
To be written
</h6>
<router-link
class="category-title ml-3"
v-for="category in groupCategories[CategoryStatus.toBeWritten]"
:key="category.id"
:to="`/haskell/${getCategoryUrl(category)}`"
>
<h6
class="ml-2 body-1 font-weight-bold"
<template v-if="groupCategories[CategoryStatus.toBeWritten]">
<h3 class="status-title">
To be written
</h3>
<router-link
class="category-title ml-3"
v-for="category in groupCategories[CategoryStatus.toBeWritten]"
:key="category.id"
:to="`/haskell/${getCategoryUrl(category)}`"
>
{{ category.title }}
</h6>
</router-link>
</router-link>
</template>
<v-btn
class="ma-0 px-1"
flat
class="ma-0 mt-1 px-1"
color="grey darken-2"
title="Add new category"
flat
@click="openAddCategoryDialog(groupName)"
>
<v-icon size="14" class="mr-1" left>$vuetify.icons.plus</v-icon>
Add new category
</v-btn>
</div>
</v-flex>
<add-category-dialog
v-model="isAddGroupDialogOpen"
:groupName="addCategoryGroupName"
/>
</v-layout>
</v-container>
</div>
</div>
<add-category-dialog
v-model="isAddGroupDialogOpen"
:groupName="addCategoryGroupName"
/>
</div>
</template>
<script lang="ts">
@ -145,15 +125,82 @@ export default class Categories extends Vue {
}
</script>
<style scoped>
<style lang="postcss" scoped>
.categories {
margin-top: 30px;
}
.categories-flex-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin: -15px -15px 0 0;
padding: 0 100px;
&:after {
content: "";
flex: 1;
}
@media screen and (max-width: 1150px) {
padding: 0 60px;
}
@media screen and (max-width: 830px) {
padding: 0 40px;
}
}
.categories-column {
margin: 15px 15px 0 0;
flex-shrink: 0;
flex-grow: 99999;
@media screen and (min-width: 1904px) {
flex-basis: calc(20% - 15px * 5);
max-width: calc(20% - 15px);
}
@media screen and (max-width: 1904px) {
flex-basis: calc(25% - 15px * 4);
max-width: calc(25% - 15px);
}
@media screen and (max-width: 1150px) {
flex-basis: calc(33.33333% - 15px * 3);
max-width: calc(33.33333% - 15px);
}
@media screen and (max-width: 830px) {
flex-basis: calc(50% - 15px * 2);
max-width: calc(50% - 15px);
}
@media screen and (max-width: 480px) {
flex-basis: calc(100% - 15px * 1);
max-width: calc(100% - 15px);
}
}
.category-group {
text-align: left;
display: flex;
flex-direction: column;
align-items: baseline;
}
.group-title {
font-weight: 900;
font-size: 1.6rem;
max-width: 90%;
word-break: break-word;
}
.category-title {
display: block;
line-height: 1.2;
font-size: 0.9rem;
font-weight: 600;
}
.category-title:not(:last-child) {
margin-bottom: 5px;
.category-title:not(:first-child) {
margin-top: 5px;
}
.status-title {
font-size: 0.9rem;
margin: 4px 0 0 8px;
line-height: 1.7;
+ .category-title {
margin-top: 0;
}
}
</style>

View File

@ -94,7 +94,7 @@ export default class Category extends Vue {
margin: 0 auto;
}
@media screend and (max-width: 768px) {
@media screen and (max-width: 768px) {
.category-item {
margin: 0 0 30px;
}

View File

@ -2,7 +2,7 @@
<div class="category-description">
<div v-if="!editDescriptionShown">
<p v-if="!categoryDescription">This category has no description yet, you can contribute to the category by adding description</p>
<div class="description-content" v-else v-html="categoryDescription" />
<div class="category-description__content" v-else v-html="categoryDescription" />
</div>
<markdown-editor
@ -44,7 +44,7 @@ import CatchConflictDecorator from 'client/helpers/CatchConflictDecorator'
},
mixins: [conflictDialogMixin]
})
export default class CategoryDescriptiom extends Vue {
export default class CategoryDescription extends Vue {
editDescriptionShown: boolean = false
originalDescription: string = _get(this, '$store.state.category.category.description.text')
modified: string = ''
@ -95,24 +95,11 @@ export default class CategoryDescriptiom extends Vue {
.category-description {
margin: 0 0 40px;
}
.category-description {
font-size: 16px;
.category-description__content > :first-child {
margin-top: 0;
}
.category-description >>> {
h1 {
font-size: 1.4rem;
margin: 15px 0;
}
h2 {
font-size: 1.2rem;
margin: 10px 0;
}
h3 {
font-size: 1rem;
margin: 5px 0;
}
.category-description__content > :last-child {
margin-bottom: 0;
}
</style>

View File

@ -1,26 +1,25 @@
<template>
<div class="category-header">
<h2 class="category-name-title" :title="categoryTitle">
<h1 class="category-name-title" :title="categoryTitle">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<a-link
openInNewTab
aria-label="RSS feed for all new items in this category"
:url="`https://guide.aelve.com/haskell/feed/category/${categoryId}`"
class="rss-link mr-1"
class="rss-link"
v-on="on"
>
<v-icon
size="24"
class="rss-link-icon"
>$vuetify.icons.rss</v-icon>
</a-link>
</template>
<span>RSS feed for all new items in this category</span>
</v-tooltip>{{categoryTitle}}
</h2>
</h1>
<div style="display: flex; align-items: center; justify-content: space-between;">
<div class="category-header__second-row">
<div class="category-group-title-wrap">
in <span :title="categoryGroup" class="category-group-title"> {{ categoryGroup }} </span>
</div>
@ -56,29 +55,27 @@
>
<v-icon
color="grey darken-2"
size="16"
size="18"
>$vuetify.icons.bars</v-icon>
</v-btn>
</template>
<v-list>
<v-list-tile class="category-actions-menu-item">
<v-list class="category-actions-menu-list">
<v-list-tile>
<CategoryHeaderBtn
text="New item"
icon="plus"
class="mr-1"
@click="openAddItemDialog"
/>
</v-list-tile>
<v-list-tile class="category-actions-menu-item">
<v-list-tile>
<CategoryHeaderBtn
text="Category settings"
icon="cog"
class="mr-1"
@click="openCategorySettingsEditDialog"
/>
</v-list-tile>
<v-list-tile class="category-actions-menu-item">
<v-list-tile>
<CategoryHeaderBtn
text="Delete category"
icon="trash-alt"
@ -161,37 +158,33 @@ export default class CategoryHeader extends Vue {
}
}
.category-name-title {
font-size: 28px;
margin-bottom: 5px;
font-size: 1.9rem;
font-weight: 700;
margin: 0 0 5px;
letter-spacing: -1px;
@media (max-width: 768px) {
font-size: 24px;
}
@media (max-width: 425px) {
font-size: 20px;
}
}
.rss-link {
/* For vertical aligning on one line with category title */
font-size: 1px;
margin-right: 6px;
}
.rss-link-icon {
@media (max-width: 768px) {
height: 20px !important;
}
@media (max-width: 425px) {
height: 18px !important;
}
height: calc(1.8rem - 5px) !important;
width: auto;
&:hover {
color: #000;
}
}
.category-header__second-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.category-group-title-wrap {
padding-left: 4px;
white-space: nowrap;
@ -202,7 +195,6 @@ export default class CategoryHeader extends Vue {
.category-group-title {
font-weight: 600;
font-size: 16px;
}
.category-actions-menu-btn {
@ -215,11 +207,10 @@ export default class CategoryHeader extends Vue {
flex: 1;
}
.category-actions-menu-item {
height: 36px;
.category-actions-menu-list {
>>> .v-list__tile {
height: 36px;
padding: 0 6px;
}
>>> button {

View File

@ -7,7 +7,7 @@
v-bind="$attrs"
v-on="$listeners"
>
<v-icon size="14" class="mr-1" left>{{`$vuetify.icons.${icon}`}}</v-icon>
<v-icon size="18" class="mr-1">{{`$vuetify.icons.${icon}`}}</v-icon>
{{ text }}
</v-btn>
</template>
@ -28,8 +28,6 @@ export default class CategoryHeaderBtn extends Vue {
.category-header-btn {
margin: 0;
padding: 0 4px;
height: 28px;
font-size: 12px;
font-weight: bold;
}
</style>

View File

@ -23,7 +23,7 @@
@save="updateSummary({original: summary.text, modified: $event})"
>
<div
class="mb-2 category-item-summary"
class="mb-2"
v-html="summary.html"
/>
</category-item-section>
@ -212,23 +212,11 @@ export default class CategoryItem extends Vue {
}
</script>
<style scoped>
<style lang="postcss" scoped>
.category-item-body {
padding: 15px 20px;
}
.category-item-body >>> p {
font-size: 16px;
}
.category-item-body >>> li {
font-size: 16px;
}
.category-item-summary >>> h1 {
margin: 25px 0 5px;
}
.category-item {
position: relative;
background: #eeeeee;
@ -237,6 +225,7 @@ export default class CategoryItem extends Vue {
.category-item-traits {
display: flex;
flex-wrap: wrap;
}
.category-item-traits > * {
@ -246,14 +235,21 @@ export default class CategoryItem extends Vue {
.category-item-traits > *:not(:last-child) {
margin-right: 20px;
@media screen and (max-width: 768px) {
margin-right: 0;
}
}
@media screend and (max-width: 768px) {
@media screen and (max-width: 768px) {
.category-item-body {
width: 100%;
}
.category-item {
margin: 0 0 30px;
}
.category-item-traits {
flex-direction: column;
}
}
</style>

View File

@ -1,16 +1,21 @@
<template>
<v-btn
:icon="!!icon"
class="ma-0"
:icon="!!icon && !showTitle"
flat
class="ma-0 font-weight-bold"
color="grey darken-2"
:style="style"
:title="title"
v-bind="$attrs"
v-on="$listeners"
>
<v-icon
v-if="icon"
color="grey darken-2"
:class="{ 'mr-1': showTitle }"
:size="iconSizeValue"
>{{ iconText }}</v-icon>
<template v-if="showTitle"> {{ title }} </template>
<slot />
</v-btn>
</template>
@ -27,6 +32,8 @@ export default class CategoryItemBtn extends Vue {
@Prop(Boolean) small: boolean
@Prop(String) size: string
@Prop(String) iconSize: string
@Prop(String) title: string
@Prop(Boolean) showTitle: boolean
get style () {
// Size prop overlaps small prop

View File

@ -1,6 +1,6 @@
<template>
<div class="category-item-section">
<h3 class="category-item-section__title title font-weight-bold mb-1">
<h3 class="category-item-section__title title font-weight-bold mb-1 mt-0">
{{ title }}
<category-item-btn
small

View File

@ -11,71 +11,168 @@
class="elevation-2"
@click.stop=""
>
<v-toolbar-title class="headline">
<router-link
:to="{hash:`item-${itemUid}`}"
class="category-item-anchor"
>#</router-link>&nbsp;<a-link
v-if="itemLink"
:url="itemLink"
openInNewTab
>{{ itemName }}</a-link>
<span v-else>{{ itemName }}</span><template v-if="this.itemHackage">&nbsp;(<a
target="_blank"
:href="`https://hackage.haskell.org/package/${this.itemHackage}`"
>Hackage</a>)</template>
<v-toolbar-title class="text-h2">
<span class="category-item-toolbar-title">
<router-link
:to="{hash:`item-${itemUid}`}"
class="category-item-anchor"
>#</router-link>
<div class="category-item-name-and-badges">
<a-link
v-if="itemLink"
class="category-item-name"
:url="itemLink"
openInNewTab
>{{ itemName }}</a-link>
<span class="category-item-name" v-else>{{ itemName }}</span>
<div class="category-item-badges">
<a
v-if="this.itemHackage"
class="text-h6 hackage-link"
target="_blank"
:href="`https://hackage.haskell.org/package/${this.itemHackage}`"
>
<v-icon
color="#fff"
class="mr-1"
size="12"
>$vuetify.icons.link</v-icon>hackage</a>
</div>
</div>
</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items class="category-item-toolbar-btns">
<category-item-btn
title="Move item up"
icon="arrow-up"
@click="moveItem('up')"
/>
<category-item-btn
title="Move item down"
icon="arrow-down"
@click="moveItem('down')"
/>
<v-toolbar-items>
<div class="category-item-toolbar-btns">
<category-item-btn
iconSize="18"
title="Move item up"
icon="arrow-up"
@click="moveItem('up')"
/>
<category-item-btn
iconSize="18"
title="Move item down"
icon="arrow-down"
@click="moveItem('down')"
/>
<category-item-btn
iconSize="18"
title="Edit item info"
icon="cog"
@click="toggleEditItemInfoMenu"
>
<v-icon
v-if="isItemInfoEdited"
class="unsaved-changes-icon"
color="#6495ed"
size="8"
>$vuetify.icons.circle</v-icon>
</category-item-btn>
<category-item-btn
title="Edit item info"
icon="cog"
@click="toggleEditItemInfoMenu"
>
<v-icon
v-if="isItemInfoEdited"
class="edit-item-info-changed-icon"
color="#6495ed"
size="8"
>$vuetify.icons.circle</v-icon>
</category-item-btn>
<category-item-btn
iconSize="18"
title="Delete item"
icon="trash-alt"
@click="deleteItem"
/>
</div>
<category-item-btn
title="Delete item"
icon="trash-alt"
@click="deleteItem"
/>
<v-menu bottom left offset-y>
<template v-slot:activator="{ on }">
<v-btn
flat
icon
title="Actions"
class="category-toolbar-mobile-menu-btn"
v-on="on"
>
<v-icon
size="18"
color="grey darken-2"
>$vuetify.icons.bars</v-icon>
</v-btn>
</template>
<v-list class="category-item-toolbar-mobile-menu-list">
<v-list-tile>
<category-item-btn
showTitle
iconSize="18"
title="Move item up"
icon="arrow-up"
@click="moveItem('up')"
/>
</v-list-tile>
<v-list-tile>
<category-item-btn
showTitle
iconSize="18"
title="Move item down"
icon="arrow-down"
@click="moveItem('down')"
/>
</v-list-tile>
<v-list-tile>
<category-item-btn
showTitle
iconSize="18"
title="Edit item info"
icon="cog"
@click="toggleEditItemInfoMenu"
>
<v-icon
v-if="isItemInfoEdited"
class="unsaved-changes-icon"
color="#6495ed"
size="8"
>$vuetify.icons.circle</v-icon>
</category-item-btn>
</v-list-tile>
<v-list-tile>
<category-item-btn
showTitle
iconSize="18"
title="Delete item"
icon="trash-alt"
@click="deleteItem"
/>
</v-list-tile>
</v-list>
</v-menu>
</v-toolbar-items>
</v-toolbar>
<v-layout column class="pa-3">
<v-flex>
<v-text-field
v-model="itemNameEdit"
label="Name"
/>
<v-text-field
v-model="itemHackageEdit"
label="Name on Hackage (optional)"
/>
<v-text-field
v-model="itemLinkEdit"
label="Site (optional)"
/>
<v-form @keydown.native.enter.ctrl="updateItemInfo">
<v-text-field
v-model="itemNameEdit"
label="Name"
/>
<v-text-field
v-model="itemHackageEdit"
label="Name on Hackage (optional)"
/>
<v-text-field
v-model="itemLinkEdit"
label="Site (optional)"
/>
</v-form>
</v-flex>
<v-flex align-self-end>
<v-btn
title="Cancel"
@click="resetAndToggleEditItemInfoMenu"
>
Cancel
</v-btn>
<v-btn
color="info"
class="mr-0"
title="Save"
:disabled="!isInfoSaveEnabled"
@ -135,7 +232,17 @@ export default class CategoryItemToolbar extends Vue {
this.isEditItemInfoMenuOpen = !this.isEditItemInfoMenuOpen
}
resetAndToggleEditItemInfoMenu () {
this.itemNameEdit = this.itemName
this.itemLinkEdit = this.itemLink
this.itemHackageEdit = this.itemHackage
this.toggleEditItemInfoMenu()
}
async updateItemInfo (): Promise<void> {
if (!this.isInfoSaveEnabled) {
return
}
await this.$store.dispatch('categoryItem/updateItemInfo', {
id: this.itemUid,
body: {
@ -172,31 +279,128 @@ export default class CategoryItemToolbar extends Vue {
}
</script>
<style scoped>
<style lang="postcss" scoped>
.category-item-toolbar {
display: flex;
margin: 0;
box-shadow: none;
>>> {
.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;
}
}
.v-expansion-panel__header {
padding: 0;
align-items: center;
cursor: unset;
}
.v-expansion-panel__body {
background: rgb(222, 222, 222);
}
}
}
.category-item-toolbar-title {
display: inline-flex;
}
.category-item-anchor {
color: rgb(151, 151, 151);
}
.edit-item-info-changed-icon {
.category-item-name-and-badges {
margin-left: 0.4rem;
}
.category-item-name {
white-space: pre-line;
word-break: break-word;
}
.category-item-badges {
display: inline-block;
margin-left: 8px;
> * {
vertical-align: middle;
display: inline-flex;
align-items: center;
padding: 0.1px 5px;
border-radius: 5px;
}
.hackage-link {
background: #5e5184;
color: #fff;
}
}
.unsaved-changes-icon {
position: absolute;
bottom: 0;
right: 5px;
}
.category-item-toolbar >>> .v-toolbar__title {
overflow: visible;
}
.category-item-toolbar {
.category-item-toolbar-btns {
display: flex;
box-shadow: none;
}
.category-item-toolbar >>> .v-expansion-panel__header {
padding: 0;
align-items: center;
cursor: unset;
flex: 1;
}
.category-item-toolbar >>> .v-expansion-panel__body {
background: #d6d6d6;
.category-toolbar-mobile-menu-btn {
display: none;
margin: 0;
}
.category-item-toolbar-btns > * {
margin: 0 2px;
.category-item-toolbar-mobile-menu-list {
>>> .v-list__tile {
height: 36px;
padding: 0 6px;
}
>>> button {
width: 100%;
padding: 5px;
.v-btn__content {
justify-content: flex-start;
}
}
}
@media (max-width: 768px) {
.category-toolbar-mobile-menu-btn {
display: block;
}
.category-item-toolbar-btns {
display: none;
}
.category-item-badges {
display: block;
margin: 5px 0 0 0;
}
.unsaved-changes-icon {
right: unset;
left: 13px;
}
}
</style>

View File

@ -202,9 +202,6 @@ export default class CategoryItemTraits extends Vue {
.category-item-trait {
padding-right: 24px;
}
.category-item-trait:not(:last-child) {
margin-bottom: 2px;
}
.category-item-edit-trait-menu {
position: absolute;
top: 0;

View File

@ -1,24 +1,22 @@
<template>
<v-dialog
lazy
:value="value"
persistent
max-width="99vw"
:value="value"
>
<slot slot="activator" />
<div class="conflict-box">
<div class="conflict-item">
<p class="title mb-2">Your version</p>
<v-card
<h2 class="mt-0">Your version</h2>
<v-card
color="#fdd"
class="conflict-content"
>
<v-card-text>{{modified}}</v-card-text>
</v-card>
<v-btn
depressed
small
class="conflict-dialog-btn"
title="Submit this version, disregard changes on server"
@click="save(modified)"
>
@ -26,37 +24,48 @@
</v-btn>
</div>
<div class="conflict-item">
<p class="title mb-2">Version on server</p>
<h2 class="mt-0">Version on server</h2>
<v-card
color="#cfc"
class="conflict-content"
>
<v-card-text>{{serverModified}}</v-card-text>
</v-card>
<v-btn
depressed
small
title="Accept this version, disregard my changes"
<v-btn
class="conflict-dialog-btn"
title="Submit this version, disregard my changes"
@click="save(serverModified)"
>
Accept this version, disregard my changes
</v-btn>
</div>
<div class="conflict-item">
<p class="title mb-2">Merged version</p>
<h2 class="mt-0">Merged version</h2>
<markdown-editor
class="mb-2"
toolbar
:value="merged"
class="conflict-content_markdown"
height="auto"
v-model="mergedEdit"
:autofocus="false"
:bottomToolbar="false"
@save="save"
/>
<v-btn
class="conflict-dialog-btn"
title="Submit merged version"
@click="save(mergedEdit)"
>
Submit the merged version
</v-btn>
</div>
</div>
</v-dialog>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import Vue from 'vue'
import Component from 'vue-class-component'
import { Prop, Watch } from 'vue-property-decorator'
import MarkdownEditor from 'client/components/MarkdownEditor.vue'
@Component({
@ -70,6 +79,13 @@ export default class ConflictDialog extends Vue {
@Prop(String) modified!: string
@Prop(String) merged!: string
mergedEdit = this.merged
@Watch('merged')
onMergedChange (newVal) {
this.mergedEdit = newVal
}
save (newValue: string) {
this.$emit('save', newValue)
}
@ -77,45 +93,88 @@ export default class ConflictDialog extends Vue {
}
</script>
<style scoped>
<style lang="postcss" scoped>
>>> .v-dialog {
overflow-x: hidden;
}
.conflict-box {
display: flex;
background: #fff;
padding: 20px;
justify-content: space-between;
}
.conflict-content {
flex: 1;
margin-bottom: 16px;
white-space: pre-wrap;
max-height: 90vh;
> *:not(:last-child) {
margin-right: 1.65rem;
}
}
.conflict-item {
width: 32%;
display: flex;
flex-flow: column;
}
max-height: 100%;
overflow: hidden;
align-items: center;
flex-direction: column;
flex: 1;
@media screen and (max-width: 1200px) {
.conflict-box {
flex-wrap: wrap;
}
.conflict-item {
width: 49%;
}
.conflict-item:nth-last-child(1) {
width: 98%;
> h2 {
text-align: center;
}
}
.conflict-content,
.conflict-content_markdown {
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
margin-bottom: 16px;
white-space: pre-wrap;
overflow: auto;
@media screen and (max-width: 768px) {
>>> {
.CodeMirror {
flex: 1;
}
.v-card__text {
word-break: break-word;
}
}
}
.conflict-dialog-btn {
display: block;
margin: 0;
min-height: 60px;
min-width: 220px;
max-height: 60px;
max-width: 220px;
padding: 6px 16px;
font-weight: bold;
font-size: 0.7rem;
>>> .v-btn__content {
width: unset;
white-space: normal;
}
}
/* Without this styling codemirror input area scrolling breaks */
.conflict-content_markdown >>> .CodeMirror-scroll {
min-height: 0 !important;
}
@media screen and (max-width: 900px) {
.conflict-box {
flex-flow: column;
flex-direction: column;
max-height: unset;
> *:not(:last-child) {
margin-right: 0;
margin-bottom: 1.65rem;
}
}
.conflict-item {
width: 100%;
/* For every content area to be the same */
.conflict-content,
.conflict-content_markdown {
max-height: 350px;
/* Without height setting codemirror input area is not scrollable */
height: 350px;
}
}
</style>

View File

@ -3,8 +3,12 @@
cause easyMDE adds new html elements next to textarea -->
<div
class="elevation-2"
:class="{
'markdown-editor_has-bottom-toolbar': bottomToolbar,
'markdown-editor_has-top-toolbar': toolbar
}"
@keydown.capture.enter="onEnterDown"
@keydown.ctrl.enter="save"
@keydown.ctrl.enter="onCtrlEnterDown"
@keydown.esc="cancel"
v-show="editor && isReady"
>
@ -14,7 +18,8 @@
flat
height="30"
color="#e5e5e5"
class="pa-2 markdown-editor-bottom-toolbar"
class="pa-2 markdown-editor__bottom-toolbar"
v-if="bottomToolbar"
v-show="editor"
>
<v-toolbar-items>
@ -55,11 +60,23 @@ export default class MarkdownEditor extends Vue {
default: ''
}) value: string
@Prop({
type: Number,
type: [Number, String],
default: 300
}) height: number
}) height: number | string
@Prop(Boolean) toolbar: boolean
@Prop(Boolean) saveOnEnter: boolean
@Prop({
type: Boolean,
default: true
}) saveOnCtrlEnter: boolean
@Prop({
type: Boolean,
default: true
}) autofocus: boolean
@Prop({
type: Boolean,
default: true
}) bottomToolbar: boolean
editor: object = null
isReady: boolean = false
@ -68,6 +85,12 @@ export default class MarkdownEditor extends Vue {
return `press${this.saveOnEnter ? ' Enter or' : ''} Ctrl+Enter to save`
}
get heightValue () {
return Number(this.height)
? this.height
: `${this.height}px`
}
@Watch('value')
onValueChange (newVal: string): void {
if (!this.editor || this.editor.value() === newVal) {
@ -91,7 +114,7 @@ export default class MarkdownEditor extends Vue {
initialValue: this.value,
spellChecker: false,
status: false,
minHeight: `${this.height}px`,
// minHeight: this.heightValue,
toolbar: this.toolbar
? [
'bold',
@ -168,14 +191,17 @@ export default class MarkdownEditor extends Vue {
if (!inputAreaEl) {
return
}
inputAreaEl.style.height = `${this.height}px`
inputAreaEl.style.height = this.heightValue
}
focusInputArea () {
if (!this.autofocus) {
return
}
// this function is triggered right after isReady set to true
// isReady controls v-show of entire markup of component
// nextTick is used cause html needs to be rendered after v-show triggered so focus will work
this.$nextTick(() => document.querySelector('.CodeMirror textarea').focus())
this.$nextTick(() => this.editor.codemirror.focus())
}
onEnterDown (event: KeyboardEvent) {
@ -185,6 +211,13 @@ export default class MarkdownEditor extends Vue {
}
}
onCtrlEnterDown (event) {
if (this.saveOnCtrlEnter) {
event.preventDefault()
this.save()
}
}
save () {
this.$emit('save', this.editor.value())
}
@ -196,18 +229,15 @@ export default class MarkdownEditor extends Vue {
</script>
<style lang="postcss" scoped>
>>> .editor-toolbar,
>>> .CodeMirror {
border: none;
border: 1px solid #bbb;
border-radius: 0;
border-bottom: 1px solid #bbb;
}
>>> .CodeMirror {
/* Fixes cutting of bottom edge of input
https://github.com/sparksuite/simplemde-markdown-editor/issues/619
*/
box-sizing: content-box;
font-size: 16px;
font-size: 1rem;
.cm-header-1 {
font-size: 2rem;
@ -229,10 +259,21 @@ export default class MarkdownEditor extends Vue {
background: unset;
}
}
.markdown-editor_has-top-toolbar >>> .CodeMirror {
border-top: 1px solid #ddd;
}
.markdown-editor_has-bottom-toolbar >>> .CodeMirror {
border-bottom: 1px solid #ddd;
}
.markdown-editor__bottom-toolbar {
border: 1px solid #bbb !important;
border-top: none !important;
}
>>> .v-toolbar__content {
padding-left: 0;
padding: 0;
}
.markdown-editor-save-tip {
font-size: 11px;
line-height: 14px;
}
</style>

View File

@ -28,6 +28,10 @@ export default class SearchField extends Vue {
setSearchInput (value: string) {
this.$store.commit('wiki/setSearchInput', value)
}
focus () {
this.$children[0].focus()
}
}
</script>

View File

@ -1,16 +1,31 @@
<template>
<v-toolbar dark app>
<v-toolbar-title>
<logo />
<logo
:class="{ 'mobile-hidden': !isSearchFieldHidden }"
/>
</v-toolbar-title>
<v-spacer></v-spacer>
<search-field />
<search-field
:class="{ 'mobile-hidden': isSearchFieldHidden }"
ref="searchField"
/>
<v-btn
flat
icon
title="Search"
color="#fff"
class="mobile-displayed"
@click="toggleSearchField"
>
<v-icon size="20">$vuetify.icons.search</v-icon>
</v-btn>
</v-toolbar>
</template>
<script lang="ts">
import Vue from 'vue'
import SearchField from 'client/components/Search.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'
@ -21,11 +36,34 @@ import axios from 'axios'
Logo
}
})
export default class Toolbar extends Vue {}
export default class Toolbar extends Vue {
isSearchFieldHidden: boolean = true
toggleSearchField () {
this.isSearchFieldHidden = !this.isSearchFieldHidden
if (!this.isSearchFieldHidden) {
this.$nextTick(() => this.$refs.searchField.focus())
}
}
}
</script>
<style scoped>
<style lang="postcss" scoped>
>>> .v-toolbar__content {
height: 64px !important;
}
.mobile-hidden {
display: block;
}
.mobile-displayed {
display: none;
}
@media screen and (max-width: 475px) {
.mobile-hidden {
display: none;
}
.mobile-displayed {
display: block;
}
}
</style>