Add filter to learning path list view, refactor code
This commit is contained in:
parent
770dbc94ea
commit
1d2224e941
|
|
@ -0,0 +1,37 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
|
||||||
|
import type { LearningContentWithCompletion, TopicType } from '@/types';
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
topic: TopicType;
|
||||||
|
nextLearningContent?: LearningContentWithCompletion;
|
||||||
|
filter?: string;
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
|
||||||
|
const filteredCircles = computed(() => {
|
||||||
|
if (props.filter === undefined || props.filter === "") {
|
||||||
|
return props.topic.circles;
|
||||||
|
}
|
||||||
|
return props.topic.circles.filter(
|
||||||
|
(circle) =>
|
||||||
|
circle.profiles.indexOf(props.filter as string) > -1 || circle.is_base_circle
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="pb-2 font-bold text-gray-700">
|
||||||
|
{{ topic.title }}
|
||||||
|
</div>
|
||||||
|
<LearningPathCircleListTile
|
||||||
|
v-for="circle in filteredCircles"
|
||||||
|
:key="circle.id"
|
||||||
|
:circle="circle"
|
||||||
|
:next-learning-content="nextLearningContent"
|
||||||
|
></LearningPathCircleListTile>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,26 +1,23 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import type { LearningContentWithCompletion, LearningPathType } from "@/types";
|
import type { LearningContentWithCompletion, LearningPathType } from "@/types";
|
||||||
|
import LearningPathListTopic from "./LearningPathListTopic.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPath: LearningPathType | undefined;
|
learningPath: LearningPathType | undefined;
|
||||||
nextLearningContent: LearningContentWithCompletion | undefined;
|
nextLearningContent: LearningContentWithCompletion | undefined;
|
||||||
|
filter?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const topics = computed(() => props.learningPath?.topics ?? []);
|
const topics = computed(() => props.learningPath?.topics ?? []);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-for="topic in topics" :key="topic.title">
|
<LearningPathListTopic
|
||||||
<div class="pb-2 font-bold text-gray-700">
|
v-for="topic in topics"
|
||||||
{{ topic.title }}
|
:key="topic.title"
|
||||||
</div>
|
:topic="topic"
|
||||||
<LearningPathCircleListTile
|
:next-learning-content="nextLearningContent"
|
||||||
v-for="circle in topic.circles"
|
:filter="filter"
|
||||||
:key="circle.id"
|
/>
|
||||||
:circle="circle"
|
|
||||||
:next-learning-content="props.nextLearningContent"
|
|
||||||
></LearningPathCircleListTile>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ const changeViewType = (viewType: ViewType) => {
|
||||||
<LearningPathListView
|
<LearningPathListView
|
||||||
:learning-path="learningPath"
|
:learning-path="learningPath"
|
||||||
:next-learning-content="lpQueryResult.nextLearningContent.value"
|
:next-learning-content="lpQueryResult.nextLearningContent.value"
|
||||||
|
:filter="filter"
|
||||||
></LearningPathListView>
|
></LearningPathListView>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {computed} from "vue";
|
||||||
|
import type { LearningContentWithCompletion, TopicType } from '@/types';
|
||||||
|
import LearningPathCircleColumn from './LearningPathCircleColumn.vue';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
topic: TopicType;
|
||||||
|
topicIndex: number;
|
||||||
|
nextLearningContent?: LearningContentWithCompletion;
|
||||||
|
overrideCircleUrlBase?: string;
|
||||||
|
filter?: string;
|
||||||
|
isLastTopic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const isFirstCircle = (circleIndex: number) =>
|
||||||
|
props.topicIndex === 0 && circleIndex === 0;
|
||||||
|
|
||||||
|
const isLastCircle = (circleIndex: number, numCircles: number) =>
|
||||||
|
props.isLastTopic && circleIndex === numCircles - 1;
|
||||||
|
|
||||||
|
|
||||||
|
const filteredCircles = computed(() => {
|
||||||
|
if (props.filter === undefined || props.filter === "") {
|
||||||
|
return props.topic.circles;
|
||||||
|
}
|
||||||
|
return props.topic.circles.filter(
|
||||||
|
(circle) =>
|
||||||
|
circle.profiles.indexOf(props.filter as string) > -1 || circle.is_base_circle
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="border-l border-gray-500"
|
||||||
|
:class="topicIndex == 0 ? 'ml-6 sm:ml-12' : ''"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
:id="`topic-${topic.slug}`"
|
||||||
|
class="inline-block h-12 self-start px-4 font-bold text-gray-800"
|
||||||
|
data-cy="lp-topic"
|
||||||
|
>
|
||||||
|
{{ topic.title }}
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-row pt-6">
|
||||||
|
<LearningPathCircleColumn
|
||||||
|
v-for="(circle, circleIndex) in filteredCircles"
|
||||||
|
:key="circle.id"
|
||||||
|
:circle="circle"
|
||||||
|
:next-learning-content="nextLearningContent"
|
||||||
|
:is-first-circle="isFirstCircle(circleIndex)"
|
||||||
|
:is-last-circle="
|
||||||
|
isLastCircle(circleIndex, topic.circles.length)
|
||||||
|
"
|
||||||
|
:override-circle-url="
|
||||||
|
overrideCircleUrlBase ? `${overrideCircleUrlBase}/${circle.slug}` : undefined
|
||||||
|
"
|
||||||
|
></LearningPathCircleColumn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
|
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
|
||||||
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
|
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
|
||||||
import { useScroll } from "@vueuse/core";
|
import { useScroll } from "@vueuse/core";
|
||||||
import { nextTick, ref, watch } from "vue";
|
import { computed, nextTick, ref, watch } from "vue";
|
||||||
import type {
|
import type {
|
||||||
LearningContentWithCompletion,
|
LearningContentWithCompletion,
|
||||||
LearningPathType,
|
LearningPathType,
|
||||||
TopicType,
|
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
|
import LearningPathPathTopic from "./LearningPathPathTopic.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPath: LearningPathType | undefined;
|
learningPath: LearningPathType | undefined;
|
||||||
|
|
@ -24,13 +24,6 @@ const scrollIncrement = 600;
|
||||||
const learnPathDiagram = ref<HTMLElement | null>(null);
|
const learnPathDiagram = ref<HTMLElement | null>(null);
|
||||||
const { x, arrivedState } = useScroll(learnPathDiagram, { behavior: "smooth" });
|
const { x, arrivedState } = useScroll(learnPathDiagram, { behavior: "smooth" });
|
||||||
|
|
||||||
const isFirstCircle = (topicIndex: number, circleIndex: number) =>
|
|
||||||
topicIndex === 0 && circleIndex === 0;
|
|
||||||
|
|
||||||
const isLastCircle = (topicIndex: number, circleIndex: number, numCircles: number) =>
|
|
||||||
topicIndex === (props.learningPath?.topics ?? []).length - 1 &&
|
|
||||||
circleIndex === numCircles - 1;
|
|
||||||
|
|
||||||
const scrollRight = () => scrollLearnPathDiagram(scrollIncrement);
|
const scrollRight = () => scrollLearnPathDiagram(scrollIncrement);
|
||||||
|
|
||||||
const scrollLeft = () => scrollLearnPathDiagram(-scrollIncrement);
|
const scrollLeft = () => scrollLearnPathDiagram(-scrollIncrement);
|
||||||
|
|
@ -39,15 +32,7 @@ const scrollLearnPathDiagram = (offset: number) => {
|
||||||
x.value += offset;
|
x.value += offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterCircles = (topic: TopicType) => {
|
const topics = computed(() => props.learningPath?.topics ?? []);
|
||||||
if (props.filter === undefined || props.filter === "") {
|
|
||||||
return topic.circles;
|
|
||||||
}
|
|
||||||
return topic.circles.filter(
|
|
||||||
(circle) =>
|
|
||||||
circle.profiles.indexOf(props.filter as string) > -1 || circle.is_base_circle
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(()=> props.filter, () => {
|
watch(()=> props.filter, () => {
|
||||||
// we need to update the scroll state of the element, otherwise the arrows won't match the scroll state
|
// we need to update the scroll state of the element, otherwise the arrows won't match the scroll state
|
||||||
|
|
@ -76,37 +61,16 @@ watch(()=> props.filter, () => {
|
||||||
ref="learnPathDiagram"
|
ref="learnPathDiagram"
|
||||||
class="no-scrollbar flex h-96 snap-x flex-row overflow-auto py-5 sm:py-10"
|
class="no-scrollbar flex h-96 snap-x flex-row overflow-auto py-5 sm:py-10"
|
||||||
>
|
>
|
||||||
<div
|
<LearningPathPathTopic
|
||||||
v-for="(topic, topicIndex) in props.learningPath?.topics ?? []"
|
v-for="(topic, topicIndex) in props.learningPath?.topics ?? []"
|
||||||
:key="topic.title"
|
:key="topic.title"
|
||||||
class="border-l border-gray-500"
|
:topic-index="topicIndex"
|
||||||
:class="topicIndex == 0 ? 'ml-6 sm:ml-12' : ''"
|
:topic="topic"
|
||||||
>
|
:next-learning-content="nextLearningContent"
|
||||||
<p
|
:override-circle-url-base="overrideCircleUrlBase"
|
||||||
:id="`topic-${topic.slug}`"
|
:filter="filter"
|
||||||
class="inline-block h-12 self-start px-4 font-bold text-gray-800"
|
:is-last-topic="topicIndex === (topics.length - 1)"
|
||||||
data-cy="lp-topic"
|
/>
|
||||||
>
|
|
||||||
{{ topic.title }}
|
|
||||||
</p>
|
|
||||||
<div class="flex flex-row pt-6">
|
|
||||||
<LearningPathCircleColumn
|
|
||||||
v-for="(circle, circleIndex) in filterCircles(topic)"
|
|
||||||
:key="circle.id"
|
|
||||||
:circle="circle"
|
|
||||||
:next-learning-content="props.nextLearningContent"
|
|
||||||
:is-first-circle="isFirstCircle(topicIndex, circleIndex)"
|
|
||||||
:is-last-circle="
|
|
||||||
isLastCircle(topicIndex, circleIndex, topic.circles.length)
|
|
||||||
"
|
|
||||||
:override-circle-url="
|
|
||||||
props.overrideCircleUrlBase
|
|
||||||
? `${props.overrideCircleUrlBase}/${circle.slug}`
|
|
||||||
: undefined
|
|
||||||
"
|
|
||||||
></LearningPathCircleColumn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<LearningPathScrollButton
|
<LearningPathScrollButton
|
||||||
v-show="!arrivedState.right"
|
v-show="!arrivedState.right"
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ textarea {
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
@apply underline underline-offset-2;
|
@apply underline underline-offset-2 cursor-pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-large {
|
.link-large {
|
||||||
|
|
@ -117,8 +117,7 @@ textarea {
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-blue-900 {
|
.filter-blue-900 {
|
||||||
filter: invert(9%) sepia(38%) saturate(5684%) hue-rotate(200deg) brightness(95%)
|
filter: invert(9%) sepia(38%) saturate(5684%) hue-rotate(200deg) brightness(95%) contrast(105%);
|
||||||
contrast(105%);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue