331 lines
10 KiB
Vue
331 lines
10 KiB
Vue
<template>
|
|
<div class="content-block__container hideable-element" :class="{'hideable-element--hidden': hidden}">
|
|
<div class="content-block" :class="specialClass">
|
|
<div class="block-actions" v-if="canEditContentBlock && editModule">
|
|
<user-widget v-bind="me" class="block-actions__user-widget content-block__user-widget"></user-widget>
|
|
<more-options-widget>
|
|
<li class="popover-links__link"><a @click="deleteContentBlock(contentBlock)">Löschen</a></li>
|
|
<li class="popover-links__link"><a @click="editContentBlock(contentBlock)">Bearbeiten</a></li>
|
|
</more-options-widget>
|
|
</div>
|
|
<div class="content-block__visibility">
|
|
<visibility-action
|
|
v-if="canEditModule"
|
|
:block="contentBlock"></visibility-action>
|
|
</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-button :after="contentBlock" v-if="canEditModule"></add-content-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 AddContentButton from '@/components/AddContentButton';
|
|
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 ModuleRoomSlug from '@/components/content-blocks/ModuleRoomSlug'
|
|
|
|
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,
|
|
'module_room_slug': ModuleRoomSlug,
|
|
Survey,
|
|
Solution,
|
|
Assignment,
|
|
Task,
|
|
AddContentButton,
|
|
VisibilityAction,
|
|
EyeIcon,
|
|
PenIcon,
|
|
TrashIcon,
|
|
MoreOptionsWidget,
|
|
UserWidget
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters(['editModule']),
|
|
canEditModule() {
|
|
return !this.contentBlock.indent && this.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 content_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 startingIndex = 0;
|
|
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
|
|
startingIndex = this.updateStartingIndex(startingIndex, contentList);
|
|
return [...newContents, ...this.createContentListOrBlocks(contentList, startingIndex)];
|
|
}
|
|
return newContents;
|
|
} else {
|
|
// handle all other items and reset current content_list if necessary
|
|
if (contentList.length !== 0) {
|
|
newContents = [...newContents, ...this.createContentListOrBlocks(contentList, startingIndex), content];
|
|
startingIndex = this.updateStartingIndex(startingIndex, contentList);
|
|
contentList = [];
|
|
return newContents;
|
|
} else {
|
|
return [...newContents, content];
|
|
}
|
|
}
|
|
}, []);
|
|
return Object.assign({}, this.contentBlock, {
|
|
contents: this.removeSingleContentListItem(newContent, startingIndex)
|
|
});
|
|
},
|
|
schoolClass() {
|
|
return this.me.selectedClass;
|
|
},
|
|
hidden() {
|
|
return isHidden(this.contentBlock, this.schoolClass);
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
editContentBlock(contentBlock) {
|
|
this.$store.dispatch('editContentBlock', contentBlock.id);
|
|
},
|
|
deleteContentBlock(contentBlock) {
|
|
const parent = this.parent;
|
|
const id = contentBlock.id;
|
|
this.$apollo.mutate({
|
|
mutation: DELETE_CONTENT_BLOCK_MUTATION,
|
|
variables: {
|
|
input: {
|
|
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, startingIndex) {
|
|
return [{
|
|
type: 'content_list',
|
|
contents: contentList,
|
|
id: contentList[0].id,
|
|
startingIndex
|
|
}];
|
|
},
|
|
updateStartingIndex(startingIndex, contentList) {
|
|
return contentList.length > 1 ? startingIndex + contentList.length : startingIndex;
|
|
},
|
|
removeSingleContentListItem(content, index) {
|
|
// just handle the case where we have one contentlistItem ( no index like "a)"" will be shown)
|
|
if (index > 0) {
|
|
return content;
|
|
};
|
|
|
|
let contentListIndex = content.findIndex(contentItem => contentItem.type === 'content_list');
|
|
if (contentListIndex < 0) {
|
|
return content;
|
|
}
|
|
return [...content.slice(0, contentListIndex), ...content[contentListIndex].contents[0].value, ...content.slice(contentListIndex + 1)];
|
|
}
|
|
},
|
|
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;
|
|
}
|
|
|
|
&__title {
|
|
line-height: 1.5;
|
|
}
|
|
|
|
&__instrument-label {
|
|
margin-bottom: 0;
|
|
@include regular-text();
|
|
}
|
|
|
|
&__action-button {
|
|
cursor: pointer;
|
|
}
|
|
|
|
&__user-widget {
|
|
margin-right: 0;
|
|
}
|
|
|
|
&--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>
|