Skip to content

Commit

Permalink
Merge pull request #329 from matematikk-mooc/TheresePersen-KURSP-901-…
Browse files Browse the repository at this point in the history
…Module-selector

Module selector menu
  • Loading branch information
manilpit authored Oct 24, 2023
2 parents e44eefd + 9848a15 commit 60ea797
Show file tree
Hide file tree
Showing 10 changed files with 1,408 additions and 487 deletions.
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
"@babel/core": "^7.22.1",
"@babel/preset-env": "^7.22.4",
"@mdi/js": "^7.2.96",
"@storybook/addon-essentials": "^7.4.0",
"@storybook/addon-interactions": "^7.4.0",
"@storybook/addon-links": "^7.4.0",
"@storybook/addon-essentials": "^7.5.1",
"@storybook/addon-interactions": "^7.5.1",
"@storybook/addon-links": "^7.5.1",
"@storybook/addon-styling": "^1.3.7",
"@storybook/blocks": "^7.4.0",
"@storybook/testing-library": "^0.2.0",
"@storybook/vue3": "^7.4.0",
"@storybook/vue3-webpack5": "^7.4.0",
"@storybook/blocks": "^7.5.1",
"@storybook/testing-library": "^0.2.2",
"@storybook/vue3": "^7.5.1",
"@storybook/vue3-webpack5": "^7.5.1",
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
"@vue/babel-plugin-jsx": "^1.1.5",
"@vue/cli": "^5.0.8",
Expand All @@ -46,7 +46,7 @@
"replace-in-file-webpack-plugin": "^1.0.6",
"sass": "^1.68.0",
"sass-loader": "^13.3.2",
"storybook": "^7.4.0",
"storybook": "^7.5.1",
"storybook-addon-sass-postcss": "^0.1.3",
"string-replace-loader": "^3.1.0",
"style-loader": "^3.3.3",
Expand Down Expand Up @@ -90,6 +90,7 @@
"dependencies": {
"@material-symbols/font-400": "^0.13.0",
"@vimeo/player": "^2.20.1",
"svg-tags": "^1.0.0",
"vue-style-loader": "^4.1.3"
}
}
162 changes: 162 additions & 0 deletions src/vue/components/course-modules/CourseModule.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<template>
<div class="module-package">
<div
class="module-package__title"
:class="{ 'module-package__title--active': isActive }"
@click="toggleCollapse"
@keydown.enter="toggleCollapse"
@keydown.space="toggleCollapse"
tabindex="0"
:aria-expanded="!collapsed"
:role="isLeaf ? 'button' : 'treeitem'"
>
<div :class="{ 'module-package__indicator--active': isActive }"></div>
<h4>
<span
class="module-package__dropdown-indicator"
:class="{ 'module-package__dropdown-indicator--collapsed': collapsed }"
>
<Icon name="expand_more" size="2em" />
</span>
<span class="title">{{ label }}</span>
</h4>
</div>

<ul
class="module-package__child-nodes"
:class="{ 'module-package__child-nodes--hidden': collapsed }"
tabindex="0"
:aria-hidden="collapsed || isLeaf"
:role="collapsed || isLeaf ? 'presentation' : 'group'"
>
<li v-for="course in nodes" :key="course.label">
<TreeView
:type="course.type"
:label="course.label"
:nodes="course.nodes"
:isCompleted="course.isCompleted"
:isActive="isActive && course.label === selectedNode"
@toggleActiveModule="toggleActiveModule"
/>
</li>
</ul>
</div>
</template>

<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue';
import Icon from '../icon/Icon.vue';
import TreeView from '../tree-view/TreeView.vue';
const props = defineProps({
type: String,
label: String,
nodes: Array,
isActive: Boolean,
});
const emits = defineEmits(['toggleActiveModule']);
const collapsed = ref(true);
const selectedNode = ref(null);
const isLeaf = computed(() => props.nodes.length === 0);
const toggleCollapse = () => {
if (!isLeaf.value) {
collapsed.value = !collapsed.value;
emits('toggleActiveModule', { module: props.label, isOpen: !collapsed.value });
}
};
const toggleActiveModule = ({module, isOpen}) => {
if (selectedNode.value === module) {
if (isOpen) {
selectedNode.value = module;
} else {
selectedNode.value = null;
}
} else {
if (isOpen) {
selectedNode.value = module;
} else {
selectedNode.value = null;
}
}
};
</script>

<style lang="scss">
@import '../../design/hide-show-effect';
@import '../../design/colors.scss';
.module-package {
border-top: 0.0625rem solid $color-grey-400;
position:relative;
&__title {
cursor: pointer;
display: flex;
align-items: center;
padding:0.5rem 1.5rem 0.5rem 1.5rem;
height: 4.375rem;
color: $color-black;
font-family: Roboto;
font-size: 1.125rem;
vertical-align: center;
position: relative;
.module-package__indicator--active{
position: absolute;
z-index:2;
border-radius: 0rem 0.4375rem 0.4375rem 0rem;
background: map-get($color-palette-green, background, 500);
width: 0.875rem;
height: 100%;
right: -0.875rem;
pointer-events: none;
@include hide-show-effect;
}
h4 {
font-weight: 400;
font-size: 1.125rem;
line-height: normal;
letter-spacing:0.063rem ;
display:flex;
align-items: center;
}
&--active {
h4 {
font-weight: 700;
}
}
}
.module-package__dropdown-indicator {
display: inline-block;
transition: transform 0.3s;
font-size: 1.125rem;
&--collapsed {
transform: rotate(-90deg);
}
}
&__child-nodes {
list-style-type: none;
background: map-get($color-palette-slate, background, 200);
padding: 0.2rem 0 0.2rem 1.5rem;
margin:0;
@include hide-show-effect;
&--hidden{
display: none;
}
}
}
</style>





25 changes: 25 additions & 0 deletions src/vue/components/course-modules/CourseModules.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { defineComponent } from "vue";
import CourseModules from "./CourseModules.vue"
import { tree_data_2_levels, tree_data_3_levels } from "./test-data";

export default {
title: "Components/CourseModules",
};
const Template = (args) =>
defineComponent({
components: { CourseModules },
setup() {
return { args };
},
template: '<CourseModules v-bind="args" />',
});

export const TwoLevelTree = Template.bind({});
TwoLevelTree.args = {
nodes: tree_data_2_levels,
};

export const ThreeLevelTree = Template.bind({});
ThreeLevelTree.args = {
nodes: tree_data_3_levels,
};
104 changes: 104 additions & 0 deletions src/vue/components/course-modules/CourseModules.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<div class="courses">
<div class="courses__header-section">
<h3 tabindex="0" class="courses__header-section__header" @keydown.enter="toggleActiveModule(null)">
<Icon name="format_list_numbered" size="1em" />
<span class="courses__header-section__header__title">Moduler</span>
</h3>
</div>
<div class="courses__treeview">
<div
class="courses__treeview__item"
v-for="(module, index) in treestructure"
:key="index"
>
<CourseModule
:type="module.type"
:label="module.label"
:nodes="module.nodes"
:isActive="isActiveModule(module.label)"
@toggleActiveModule="toggleActiveModule"
/>
</div>
</div>
</div>
</template>

<script setup>
import { defineProps, ref } from 'vue';
import Icon from '../icon/Icon.vue';
import CourseModule from './CourseModule.vue';
const props = defineProps({
nodes: Array,
});
const treestructure = props.nodes; // Assign nodes prop to treestructure
const selectedNode = ref(null);
const toggleActiveModule = ({module, isOpen}) => {
if (selectedNode.value === module) {
if (isOpen) {
selectedNode.value = module;
} else {
selectedNode.value = null;
}
} else {
if (isOpen) {
selectedNode.value = module;
} else {
selectedNode.value = null;
}
}
};
const isActiveModule = (nodeLabel) => {
return nodeLabel === selectedNode.value;
};
</script>


<style lang="scss">
@import '../../design/box-shadow';
@import '../../design/colors.scss';
.courses {
width: 100%;
max-width: 35rem;
border-radius: 1.6875rem 0rem 0rem 1.6875rem;
border: 0.0625rem solid $color-grey-400;
background: $color-white;
margin: 0 1rem 0 1rem;
padding: 0 0 0.75rem 0;
@include box-shadow(medium);
&__header-section {
color: black;
word-wrap: break-word;
border-bottom: 0.125rem solid $color-grey-400;
padding: 1.75rem 1rem 0.625rem 1.5rem;
&__header{
display:flex;
align-items: center;
justify-content: flex-start;
font-size: 1.25rem;
font-family: Roboto;
font-weight: 600;
margin-left: 1rem;
&__title{
margin-left:1.5rem;
margin-top: -0.5rem;
}
}
}
&__treeview {
display: flex;
flex-direction: column;
justify-content: flex-start;
padding-bottom:1rem;
}
}
</style>
Loading

0 comments on commit 60ea797

Please sign in to comment.