369 lines
9.7 KiB
Vue
369 lines
9.7 KiB
Vue
<template>
|
|
<div
|
|
:class="{'hideable-element--greyed-out': hidden}"
|
|
class="content-block__container hideable-element content-list__parent"
|
|
>
|
|
<div
|
|
:class="specialClass"
|
|
:style="instrumentStyle"
|
|
class="content-block"
|
|
data-cy="content-block"
|
|
>
|
|
<div
|
|
class="block-actions"
|
|
v-if="canEditContentBlock && editMode"
|
|
>
|
|
<user-widget
|
|
v-bind="me"
|
|
class="block-actions__user-widget content-block__user-widget"
|
|
/>
|
|
<more-options-widget>
|
|
<li class="popover-links__link">
|
|
<popover-link
|
|
data-cy="delete-content-block-link"
|
|
text="Löschen"
|
|
@link-action="deleteContentBlock(contentBlock)"
|
|
/>
|
|
</li>
|
|
|
|
<li class="popover-links__link">
|
|
<popover-link
|
|
text="Bearbeiten"
|
|
@link-action="editContentBlock(contentBlock)"
|
|
/>
|
|
</li>
|
|
</more-options-widget>
|
|
</div>
|
|
<div class="content-block__visibility">
|
|
<visibility-action
|
|
:block="contentBlock"
|
|
v-if="canEditModule"
|
|
/>
|
|
</div>
|
|
|
|
<h3
|
|
class="content-block__instrument-label"
|
|
data-cy="instrument-label"
|
|
:style="instrumentLabelStyle"
|
|
v-if="instrumentLabel !== ''"
|
|
>
|
|
{{ instrumentLabel }}
|
|
</h3>
|
|
<h4
|
|
class="content-block__title"
|
|
v-if="!contentBlock.indent"
|
|
>
|
|
{{ contentBlock.title }}
|
|
</h4>
|
|
|
|
<content-component
|
|
:component="component"
|
|
:root="root"
|
|
:parent="contentBlock"
|
|
:bookmarks="contentBlock.bookmarks"
|
|
:notes="contentBlock.notes"
|
|
:edit-mode="editMode"
|
|
v-for="component in contentBlocksWithContentLists.contents"
|
|
:key="component.id"
|
|
/>
|
|
</div>
|
|
|
|
<add-content-button
|
|
:where="{after: contentBlock}"
|
|
v-if="canEditModule"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import AddContentButton from '@/components/AddContentButton';
|
|
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
|
import UserWidget from '@/components/UserWidget';
|
|
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
|
|
|
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
|
|
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
|
|
|
import me from '@/mixins/me';
|
|
|
|
import {hidden} from '@/helpers/visibility';
|
|
import {CONTENT_TYPE} from '@/consts/types';
|
|
import PopoverLink from '@/components/ui/PopoverLink';
|
|
import {removeAtIndex} from '@/graphql/immutable-operations';
|
|
import {EDIT_CONTENT_BLOCK_PAGE} from '@/router/module.names';
|
|
import {instrumentCategory} from '@/helpers/instrumentType';
|
|
|
|
const ContentComponent = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentComponent');
|
|
|
|
|
|
export default {
|
|
name: 'ContentBlock',
|
|
props: {
|
|
contentBlock: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
parent: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
editMode: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
},
|
|
|
|
mixins: [me],
|
|
|
|
components: {
|
|
PopoverLink,
|
|
ContentComponent,
|
|
AddContentButton,
|
|
VisibilityAction,
|
|
MoreOptionsWidget,
|
|
UserWidget,
|
|
},
|
|
|
|
computed: {
|
|
canEditModule() {
|
|
return !this.contentBlock.indent && this.editMode;
|
|
},
|
|
specialClass() {
|
|
return `content-block--${this.contentBlock.type.toLowerCase()}`;
|
|
},
|
|
isInstrumentBlock() {
|
|
return !!this.contentBlock.instrumentCategory;
|
|
},
|
|
instrumentStyle() {
|
|
if (this.isInstrumentBlock) {
|
|
return {
|
|
backgroundColor: this.contentBlock.instrumentCategory.background
|
|
};
|
|
}
|
|
return {};
|
|
},
|
|
instrumentLabel() {
|
|
const contentType = this.contentBlock.type.toLowerCase();
|
|
if (contentType.startsWith('base')) { // all legacy instruments start with `base`
|
|
return instrumentCategory(contentType);
|
|
}
|
|
if (this.isInstrumentBlock) {
|
|
return instrumentCategory(this.contentBlock.instrumentCategory.name);
|
|
}
|
|
return '';
|
|
},
|
|
instrumentLabelStyle() {
|
|
if (this.isInstrumentBlock) {
|
|
return {
|
|
color: this.contentBlock.instrumentCategory.foreground
|
|
};
|
|
}
|
|
return {};
|
|
},
|
|
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
|
|
}
|
|
*/
|
|
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
|
|
let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)];
|
|
return updatedContent;
|
|
}
|
|
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,
|
|
});
|
|
},
|
|
hidden() {
|
|
return hidden({
|
|
block: this.contentBlock,
|
|
schoolClass: this.schoolClass,
|
|
type: CONTENT_TYPE,
|
|
});
|
|
},
|
|
root() {
|
|
// we need the root content block id, not the generated content block if inside a content list block
|
|
return this.contentBlock.root ? this.contentBlock.root : this.contentBlock.id;
|
|
},
|
|
},
|
|
methods: {
|
|
editContentBlock(contentBlock) {
|
|
const route = {
|
|
name: EDIT_CONTENT_BLOCK_PAGE,
|
|
params: {
|
|
id: contentBlock.id,
|
|
},
|
|
};
|
|
this.$router.push(route);
|
|
},
|
|
deleteContentBlock(contentBlock) {
|
|
this.$modal.open('confirm').then(() => {
|
|
this.doDeleteContentBlock(contentBlock);
|
|
})
|
|
.catch();
|
|
},
|
|
doDeleteContentBlock(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}}}) {
|
|
if (success) {
|
|
const query = CHAPTER_QUERY;
|
|
const variables = {
|
|
id: parent.id,
|
|
};
|
|
const {chapter} = store.readQuery({query, variables});
|
|
const index = chapter.contentBlocks.findIndex(contentBlock => contentBlock.id === id);
|
|
const contentBlocks = removeAtIndex(chapter.contentBlocks, index);
|
|
const data = {
|
|
chapter: {
|
|
...chapter,
|
|
contentBlocks,
|
|
},
|
|
};
|
|
store.writeQuery({query, variables, data});
|
|
}
|
|
},
|
|
});
|
|
},
|
|
createContentListOrBlocks(contentList) {
|
|
return [{
|
|
type: 'content_list',
|
|
contents: contentList,
|
|
id: contentList[0].id,
|
|
}];
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@import "~styles/helpers";
|
|
|
|
.content-block {
|
|
margin-bottom: $section-spacing;
|
|
position: relative;
|
|
|
|
&__container {
|
|
position: relative;
|
|
}
|
|
|
|
&__title {
|
|
line-height: 1.5;
|
|
margin-top: -0.5rem; // to offset the 1.5 line height, it leaves a padding on top
|
|
}
|
|
|
|
&__instrument-label {
|
|
margin-bottom: $medium-spacing;
|
|
@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;
|
|
margin-bottom: $large-spacing;
|
|
@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;
|
|
}
|
|
}
|
|
|
|
&--base_interdisciplinary {
|
|
@include content-box($color-accent-4-list);
|
|
|
|
.content-block__instrument-label {
|
|
color: $color-accent-4-dark;
|
|
}
|
|
}
|
|
|
|
&--instrument {
|
|
@include content-box-base;
|
|
}
|
|
|
|
/deep/ p {
|
|
line-height: 1.5;
|
|
margin-bottom: 1em;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
/deep/ .text-block {
|
|
ul {
|
|
@include list-parent;
|
|
}
|
|
|
|
li {
|
|
@include list-child;
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
|
|
}
|
|
</style>
|