skillbox/client/src/components/ContentBlock.vue

349 lines
10 KiB
Vue

<template>
<div class="content-block__container" :class="{'content-block__container--hidden': hidden}">
<div class="content-block" :class="specialClass">
<div class="content-block__actions" v-if="canEditContentBlock && editModule">
<user-widget v-bind="me" class="content-block__user-widget"></user-widget>
<more-options-widget>
<li class="popover-links__link"><a @click="deleteContentBlock()">Löschen</a></li>
<li class="popover-links__link"><a @click="editContentBlock()">Bearbeiten</a></li>
</more-options-widget>
</div>
<div class="content-block__visibility" v-if="editModule">
<visibility-action
v-if="!contentBlock.indent"
:block="contentBlock"></visibility-action>
<!--<a @click="editContentBlock()" v-if="canEditContentBlock" class="content-block__action-button">-->
<!--<pen-icon class="content-block__action-icon action-icon"></pen-icon>-->
<!--</a>-->
<!--<a @click="deleteContentBlock(contentBlock.id)" v-if="canEditContentBlock" class="content-block__action-button">-->
<!--<trash-icon class="content-block__action-icon action-icon"></trash-icon>-->
<!--</a>-->
</div>
<h3 v-if="instrumentLabel !== ''" class="content-block__instrument-label">{{instrumentLabel}}</h3>
<h4 class="content-block__title" v-if="!contentBlock.indent">{{contentBlock.title}}</h4>
<component v-for="component in contentBlocksWithContentLists.contents"
:key="component.id"
:is="component.type"
v-bind="component">
</component>
</div>
<add-content-block-button :after="contentBlock.id"
v-if="!contentBlock.indent && editModule"></add-content-block-button>
</div>
</template>
<script>
import TextBlock from '@/components/content-blocks/TextBlock';
import InstrumentWidget from '@/components/content-blocks/InstrumentWidget';
import Task from '@/components/content-blocks/Task';
import ImageBlock from '@/components/content-blocks/ImageBlock';
import ImageUrlBlock from '@/components/content-blocks/ImageUrlBlock';
import VideoBlock from '@/components/content-blocks/VideoBlock';
import LinkBlock from '@/components/content-blocks/LinkBlock';
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
import InfogramBlock from '@/components/content-blocks/InfogramBlock';
import GeniallyBlock from '@/components/content-blocks/GeniallyBlock';
import SubtitleBlock from '@/components/content-blocks/SubtitleBlock';
import ContentListBlock from '@/components/content-blocks/ContentListBlock';
import Assignment from '@/components/content-blocks/assignment/Assignment';
import Survey from '@/components/content-blocks/SurveyBlock';
import Solution from '@/components/content-blocks/Solution';
import AddContentBlockButton from '@/components/AddContentBlockButton';
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
import UserWidget from '@/components/UserWidget';
import VisibilityAction from '@/components/visibility/VisibilityAction';
import EyeIcon from '@/components/icons/EyeIcon';
import PenIcon from '@/components/icons/PenIcon';
import TrashIcon from '@/components/icons/TrashIcon';
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
import {meQuery} from '@/graphql/queries';
import {mapGetters} from 'vuex';
import {isHidden} from '@/helpers/content-block';
const instruments = {
base_communication: 'Sprache & Kommunikation',
base_society: 'Gesellschaft'
};
export default {
props: ['contentBlock', 'parent'],
name: 'content-block',
components: {
'text_block': TextBlock,
'basic_knowledge': InstrumentWidget, // for legacy
'instrument': InstrumentWidget,
'image_block': ImageBlock,
'image_url_block': ImageUrlBlock,
'video_block': VideoBlock,
'link_block': LinkBlock,
'document_block': DocumentBlock,
'infogram_block': InfogramBlock,
'genially_block': GeniallyBlock,
'subtitle': SubtitleBlock,
'content_list': ContentListBlock,
Survey,
Solution,
Assignment,
Task,
AddContentBlockButton,
VisibilityAction,
EyeIcon,
PenIcon,
TrashIcon,
MoreOptionsWidget,
UserWidget
},
computed: {
...mapGetters(['editModule']),
specialClass() {
return `content-block--${this.contentBlock.type.toLowerCase()}`
},
instrumentLabel() {
const contentType = this.contentBlock.type.toLowerCase()
if (!(contentType in instruments)) {
return ''
}
return `Instrument - ${instruments[contentType]}`
},
canEditContentBlock() {
return this.contentBlock.mine && !this.contentBlock.indent;
},
contentBlocksWithContentLists() {
/*
collects all conent_list_items in content_lists:
{
text_block,
content_list_item: [contents...],
content_list_item: [contents...],
text_block
} becomes
{
text_block,
content_list: [content_list_item: [contents...], content_list_item: [contents...]],
text_block
}
if there's only a single content_list_item it should not be displayed as list like so
{
text_block,
content_list_item: [text_block, image_block],
} becomes
{
text_block,
text_block,
image_block
}
*/
let contentList = [];
let newContent = this.contentBlock.contents.reduce((newContents, content, index) => {
// collect content_list_items
if (content.type === 'content_list_item') {
contentList = [...contentList, content]
if (index === this.contentBlock.contents.length - 1) { // content is last element of contents array
return [...newContents, ...this.createContentListOrBlocks(contentList)];
}
return newContents
} else {
// handle all other items and reset current content_list if necessary
if (contentList.length !== 0) {
newContents = [...newContents, ...this.createContentListOrBlocks(contentList), content];
contentList = [];
return newContents;
} else {
return [...newContents, content]
}
}
}, []);
return Object.assign({}, this.contentBlock, {
contents: newContent
});
},
schoolClass() {
return this.me.selectedClass;
},
hidden() {
return isHidden(this.contentBlock, this.schoolClass);
}
},
methods: {
editContentBlock() {
this.$store.dispatch('editContentBlock', this.contentBlock.id);
},
deleteContentBlock(id) {
const parent = this.parent;
this.$apollo.mutate({
mutation: DELETE_CONTENT_BLOCK_MUTATION,
variables: {
input: {
id: id
}
},
update(store, {data: {deleteContentBlock: {success}}}) {
try {
if (success) {
const query = CHAPTER_QUERY;
const variables = {
id: parent
};
const data = store.readQuery({query, variables});
data.chapter.contentBlocks.edges.splice(data.chapter.contentBlocks.edges.findIndex(edge => edge.node.id === id), 1);
store.writeQuery({query, variables, data});
}
} catch (e) {
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
}
}
});
},
createContentListOrBlocks(contentList) {
// if list contains only one item, return blocks
if (contentList.length === 1) {
return contentList[0].value;
}
return [{
type: 'content_list',
contents: contentList,
id: contentList[0].id
}];
},
},
data() {
return {
showVisibility: false,
me: {}
}
},
apollo: {
me: meQuery
}
}
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.content-block {
margin-bottom: 2.5em;
position: relative;
&__container {
position: relative;
&--hidden {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(255, 255, 255, 0.5);
z-index: 10;
}
}
}
&__title {
line-height: 1.5;
}
&__instrument-label {
margin-bottom: 0;
@include regular-text();
}
&__visibility {
position: absolute;
left: -70px;
top: -4px;
display: grid;
}
&__actions {
position: absolute;
top: 10px;
right: -85px;
display: flex;
flex-direction: column;
align-items: center;
}
&__user-widget {
margin-right: 0;
margin-bottom: $small-spacing;
}
&__action-button {
cursor: pointer;
}
&--base_communication {
@include content-box($color-accent-1-list);
.content-block__instrument-label {
color: $color-accent-1-dark;
}
}
&--task {
@include light-border(bottom);
.content-block__title {
color: $color-brand;
margin-top: $default-padding;
@include light-border(bottom);
@include desktop {
margin-top: 0;
}
}
}
&--base_society {
@include content-box($color-accent-2-list);
.content-block__instrument-label {
color: $color-accent-2-dark;
}
}
/deep/ p {
line-height: 1.5;
margin-bottom: 1em;
&:last-child {
margin-bottom: 0;
}
}
/deep/ .text-block {
ul {
padding-left: 25px;
}
li {
list-style: disc;
line-height: 1.5;
}
}
}
</style>