Skip to content
Snippets Groups Projects
Select Git revision
  • 0f4050b6139089d808adac44b9a439ad963fc91c
  • main default protected
  • studip-rector
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • pipeline-improvements
  • webpack-optimizations
  • rector
  • icon-renewal
  • http-client-and-factories
  • jsonapi-atomic-operations
  • vueify-messages
  • tic-2341
  • 135-translatable-study-areas
  • extensible-sorm-action-parameters
  • sorm-configuration-trait
  • jsonapi-mvv-routes
  • docblocks-for-magic-methods
19 results

courseware.module.js

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    CoursewareTree.vue 11.08 KiB
    <template>
        <div class="cw-tree" ref="tree">
            <template v-if="editMode">
                    <span aria-live="assertive" class="assistive-text">{{ assistiveLive }}</span>
                    <span id="operation" class="assistive-text">
                        {{$gettext('Drücken Sie die Leertaste, um neu anzuordnen.')}}
                    </span>
            </template>
            <ol v-if="!processing" class="cw-tree-root-list" role="listbox">
                <courseware-tree-item
                    class="cw-tree-item"
                    :element="rootElementWithNestedChildren"
                    :currentElement="currentElement"
                    @sort="sort"
                    @moveItemUp="moveItemUp"
                    @moveItemDown="moveItemDown"
                    @moveItemPrevLevel="moveItemPrevLevel"
                    @moveItemNextLevel="moveItemNextLevel"
                    @childrenUpdated="updateNestedChildren"
                ></courseware-tree-item>
            </ol>
            <studip-progress-indicator
                v-else 
                :description="$gettext('Vorgang wird bearbeitet...')"
            />
        </div>
    </template>
    
    <script>
    import CoursewareTreeItem from './CoursewareTreeItem.vue';
    import StudipProgressIndicator from '../StudipProgressIndicator.vue';
    
    import { mapActions, mapGetters } from 'vuex';
    
    export default {
        components: { 
            CoursewareTreeItem,
            StudipProgressIndicator
        },
        name: 'courseware-tree',
        data() {
            return {
                nestedChildren: [],
                processing: false,
                rootElementWithNestedChildren: {},
            }
        },
        computed: {
            ...mapGetters({
                context: 'context',
                courseware: 'courseware',
                relatedStructuralElement: 'courseware-structural-elements/related',
                structuralElementById: 'courseware-structural-elements/byId',
                childrenById: 'courseware-structure/children',
                viewMode: 'viewMode',   
                structuralElements: 'courseware-structural-elements/all',
                assistiveLive: 'assistiveLiveContents'
            }),
            currentElement() {
                const id = this.$route?.params?.id;
                if (!id) {
                    return null;
                }
    
                return this.structuralElementById({ id }) ?? null;
            },
    
            rootElement() {
                if (this.context.type !== 'public') {
                        const root = this.relatedStructuralElement({
                        parent: { id: this.courseware.id, type: this.courseware.type },
                        relationship: 'root',
                    });
    
                    return root;
                } else {
                    return this.structuralElementById({ id: this.context.rootId });
                }
    
            },
            editMode() {
                return this.viewMode === 'edit';
            },
        },
        methods: {
             ...mapActions({
                updateStructuralElement: 'updateStructuralElement',
                lockObject: 'lockObject',
                unlockObject: 'unlockObject',
                sortChildrenInStructualElements: 'sortChildrenInStructualElements',
                loadStructuralElement: 'loadStructuralElement',
                setAssistiveLiveContents: 'setAssistiveLiveContents'
             }),
            updateNestedChildren() {
                this.nestedChildren = this.getNestedChildren(this.rootElement);
                this.setRootElementWithNestedChildren();
            },
            setRootElementWithNestedChildren() {
                let element = { id: this.rootElement.id, attributes: this.rootElement.attributes };
                element.nestedChildren = this.nestedChildren;
    
                this.rootElementWithNestedChildren = element;
            },
            getNestedChildren(structuralElement) {
                let children = _.cloneDeep(this.structuralElements
                    .filter(
                        element => element.relationships.parent?.data?.id === structuralElement.id
                    )).sort((a,b) => a.attributes.position - b.attributes.position);
    
                let nestedChildren = [];
                for (let child of children) {
                    child.nestedChildren = this.getNestedChildren(child);
                    nestedChildren.push(child);
                }
    
                return nestedChildren;
            },
            async sort(data) {
                const tree = this.$refs.tree;
                const currentScrollPosition = tree.offsetParent.scrollTop;
                this.processing = true;
                if (data.oldParent !== data.newParent) {
                    await this.lockObject({ id: data.id, type: 'courseware-structural-elements' });
                    let element = this.structuralElementById({ id: data.id });
                    element.relationships.parent.data.id = data.newParent;
                    await this.updateStructuralElement({
                        element: element,
                        id: element.id,
                    });
                    await this.unlockObject({ id: data.id, type: 'courseware-structural-elements' });
                    await this.loadStructuralElement(data.id);
                }
                await this.loadStructuralElement(data.newParent);
                const parent = this.structuralElementById({ id: data.newParent });
                await this.sortChildrenInStructualElements({parent: parent, children: data.sortArray});
                this.updateNestedChildren();
                this.processing = false;
                this.$nextTick(() => {
                    tree.offsetParent.scrollTop = currentScrollPosition;
                });
            },
            moveItemUp(data) {
                data.direction = 'up';
                this.reorderNestedChildren(data);
            },
            moveItemDown(data) {
                data.direction = 'down';
                this.reorderNestedChildren(data);
            },
            moveItemPrevLevel(data) {
                data.direction = 'prev';
                this.reorderNestedChildren(data);
            },
            moveItemNextLevel(data) {
                data.direction = 'next';
                this.reorderNestedChildren(data);
            },
            reorderNestedChildren(data) {
                this.rootElementWithNestedChildren = this.recursiveNestedChildrenUpdate(this.rootElementWithNestedChildren, data);
            },
            recursiveNestedChildrenUpdate(element, data) {
                if (data.direction === 'prev' && data.parents[1] && element.id === data.parents[1]) {
                    //element is grandparent
                    let parentIndex = element.nestedChildren.findIndex((e) => e.id === data.parents[0]);
                    let movingElementIndex = element.nestedChildren[parentIndex].nestedChildren.findIndex((e) => e.id === data.element.id);
                    const newPos = parentIndex + 1;
                    element.nestedChildren.splice(newPos, 0, element.nestedChildren[parentIndex].nestedChildren[movingElementIndex]);
                    element.nestedChildren[parentIndex].nestedChildren.splice(movingElementIndex, 1);
    
                    element.nestedChildren[newPos].newPos = newPos;
                    element.nestedChildren[newPos].newParentId =  parseInt(element.id);
                    element.nestedChildren[newPos].sortArray = element.nestedChildren.map(c => {return {id: c.id, type: 'courseware-structural-elements'}});
                    element.nestedChildren[newPos].moveDirection = data.direction;
    
                    const assistiveLive = this.$gettextInterpolate(
                        this.$gettext('%{elementTitle} eine Ebene nach oben bewegt. Übergeordnete Seite: %{parentTitle}. Aktuelle Position in der Liste: %{pos} von %{listLength}'), 
                        { elementTitle: data.element.attributes.title, parentTitle: element.attributes.title, pos: newPos + 1, listLength: element.nestedChildren[newPos].sortArray.length }
                    );
                    this.setAssistiveLiveContents(assistiveLive);
                }
                if (element.id === data.parents[0]) {
                    if (data.direction === 'up' || data.direction === 'down') {
                        const elementIndex = element.nestedChildren.findIndex((e) => e.id === data.element.id);
                        let vertical = data.direction === 'up' ? -1 : data.direction === 'down' ? 1 : 0;
                        const newPos = elementIndex + vertical;
                        if (newPos >= 0 && newPos < element.nestedChildren.length) {
                            element.nestedChildren.splice(newPos, 0, element.nestedChildren.splice(elementIndex, 1)[0]);
                            element.nestedChildren[newPos].newPos = newPos;
                            element.nestedChildren[newPos].newParentId = parseInt(element.id);
                            element.nestedChildren[newPos].sortArray = element.nestedChildren.map(c => {return {id: c.id, type: 'courseware-structural-elements'}});
                            element.nestedChildren[newPos].moveDirection = data.direction;
    
                            const assistiveLive = this.$gettextInterpolate(
                                this.$gettext('%{elementTitle} bewegt. Aktuelle Position in der Liste: %{pos} von %{listLength}'), 
                                { elementTitle: data.element.attributes.title, pos: newPos + 1, listLength: element.nestedChildren[newPos].sortArray.length }
                            );
                            this.setAssistiveLiveContents(assistiveLive);
                        }
                    }
                    if (data.direction === 'next') {
                        const elementIndex = element.nestedChildren.findIndex((e) => e.id === data.element.id);
                        if (elementIndex !== 0) {
                            const newParentIndex = elementIndex - 1;
                            element.nestedChildren[newParentIndex].nestedChildren.push(element.nestedChildren[elementIndex]);
                            element.nestedChildren.splice(elementIndex, 1);
                            const newPos = element.nestedChildren[newParentIndex].nestedChildren.length - 1;
                            element.nestedChildren[newParentIndex].nestedChildren[newPos].newPos = newPos;
                            const newParentId = element.nestedChildren[newParentIndex].id;
                            element.nestedChildren[newParentIndex].nestedChildren[newPos].newParentId = parseInt(newParentId);
                            element.nestedChildren[newParentIndex].nestedChildren[newPos].sortArray = element.nestedChildren[newParentIndex].nestedChildren.map(c => {return {id: c.id, type: 'courseware-structural-elements'}});
    
                            const assistiveLive = this.$gettextInterpolate(
                                this.$gettext('%{elementTitle} eine Ebene nach unten bewegt. Übergeordnete Seite: %{parentTitle}. Aktuelle Position in der Liste: %{pos} von %{listLength}'), 
                                { elementTitle: data.element.attributes.title, parentTitle: element.nestedChildren[newParentIndex].attributes.title, pos: newPos + 1, listLength: element.nestedChildren[newParentIndex].nestedChildren[newPos].sortArray.length }
                            );
                            this.setAssistiveLiveContents(assistiveLive);
                        }
                    }
                } else {
                    element.nestedChildren.forEach((child,index) => {
                        element.nestedChildren[index] = this.recursiveNestedChildrenUpdate(child, data);
                    });
                }
    
                return element;
            },
        },
        mounted() {
            this.updateNestedChildren();
        }
    };
    </script>