Path: blob/main/frontend/vue/components/UtilityPanel/UtilityPanel.vue
4038 views
<template>
<section class="utility-panel" :class="{ 'utility-panel_open': isVisible, 'utility-panel_closed': !isVisible }">
<UtilityPanelHeader
ref="panelHeader"
class="utility-panel__header"
:label="getLabel()"
@updatePanelStatus="togglePanel"
@selectedPanelTitle="getSelectedPanelTitle"
/>
<UtilityPanelContent
ref="panelContent"
class="utility-panel__content"
:selection="selectedPanelTitle"
:notations-data="filteredNotations"
:vocabulary-data="filteredVocabulary"
@emptyStateRedirect="redirectToPanel"
/>
</section>
</template>
<script lang="ts">
import { Vue, Options, prop } from 'vue-class-component'
import UtilityPanelHeader from './UtilityPanelHeader.vue'
import UtilityPanelContent from './UtilityPanelContent.vue'
export interface Notation {
html: string
say?: string
meaning: string
type?: string
sections: any[]
}
interface NotationsJson {
[key: string] : Notation
}
export interface Term {
title: string
text: string[]
sections: any[]
}
interface TermsJson {
[key: string] : Term
}
class Props {
notations = prop<Notation>({})
glossaryTerms = prop<Term>({})
}
@Options({
components: { UtilityPanelHeader, UtilityPanelContent },
computed: {
filteredNotations (): Notation[] {
const data = document.getElementById('notations')
const sectionNode = document.querySelector('x-course')
const finalNotations: Notation[] = []
if (data && sectionNode) {
const sectionTitle = sectionNode.getAttribute('data-section')
const notationsParsed = JSON.parse(data.innerHTML) as NotationsJson
const notationsKeys = Object.keys(notationsParsed).filter(key => !key.startsWith('_'))
const notationsArr = notationsKeys.map(key => notationsParsed[key])
notationsArr.forEach((item: Notation) => {
if (item.sections.includes(sectionTitle)) {
finalNotations.push(item)
}
})
}
this.notationsData = finalNotations
return finalNotations
},
filteredVocabulary ():Term[] {
const data = document.getElementById('glossary')
const sectionNode = document.querySelector('x-course')
const finalVocabulary: Term[] = []
if (data && sectionNode) {
const vocabularyParsed = JSON.parse(data.innerHTML) as TermsJson
const vocabularyArr = Object.values(vocabularyParsed)
const sectionTitle = sectionNode.getAttribute('data-section')
vocabularyArr.forEach((item) => {
if (item.sections.includes(sectionTitle)) {
finalVocabulary.push(item)
}
})
}
this.vocabData = finalVocabulary
return finalVocabulary
}
},
methods: {
getSelectedPanelTitle (val: any) {
const refPanelContent: any = this.$refs.panelContent
this.selectedPanelTitle = val
refPanelContent.chooseTitle(val)
}
}
})
export default class UtilityPanel extends Vue.with(Props) {
isVisible = true;
selectedPanelTitle:string = ''
isMobile = false;
notationsData = []
vocabData = []
mounted () {
this.detectSmallScreen()
}
detectSmallScreen () {
const rootComponent = this
window.addEventListener('load', function () {
const HTMLFrame = document.getElementsByTagName('html')[0]
const mobileDetected = HTMLFrame.classList.contains('is-mobile')
if (window.innerWidth <= 1056 || mobileDetected) {
rootComponent.isVisible = false
rootComponent.isMobile = true
}
// handling tablet use case
if (window.innerWidth >= 672 && window.innerWidth <= 1056) {
rootComponent.isVisible = true
rootComponent.isMobile = true
}
})
}
togglePanel (val: boolean) {
this.isVisible = val
}
getLabel () {
if (this.isVisible) {
return 'Hide'
} else {
return 'Show details'
}
}
redirectToPanel (e:string) {
const refPanelContent: any = this.$refs.panelContent
const refPanelHeader: any = this.$refs.panelHeader
this.selectedPanelTitle = e
refPanelContent.chooseTitle(e)
refPanelHeader.getUpdatedPanelSelection(e)
}
}
</script>
<style lang="scss">
@use 'sass:math';
@import 'carbon-components/scss/globals/scss/typography';
@import '../../../scss/variables/settings.scss';
@import '../../../scss/variables/colors.scss';
@import '../../../scss/variables/mq.scss';
.utility-panel {
background-color: $background-color-white;
width: 100%;
height: auto;
user-select: text;
@include mq($until: small) {
top: 6rem;
}
&__header {
flex: 0 0 auto;
}
&__content {
flex: 1 0 0;
overflow: auto;
padding-bottom: $spacing-13;
}
&_open {
width: 100%;
border-left: 1px solid $border-color;
max-width: $right-sidebar-width-xl;
min-height: 100vh;
flex-direction: column;
display: flex;
@include mq($from: x-large) {
max-width: $right-sidebar-width-xl;
}
@include mq($from: large, $until: x-large) {
max-width: $right-sidebar-width-lg;
}
@include mq($from: medium, $until: large) {
max-width: math.div($right-sidebar-width-xl, 1.5);
}
@include mq($from: max-size) {
max-width: $right-sidebar-width-max;
}
@include mq($until: medium) {
max-width: initial;
.app-cta__content {
display: none;
}
.app-cta {
width: 3.25rem;
}
.utility-panel-header {
border-bottom: 1px solid $border-color;
}
}
p:hover {
cursor: text;
}
}
&_closed {
width: 10rem;
background-color: transparent;
.utility-panel-dropdown,
.utility-panel-content {
display: none;
}
.utility-panel-header {
justify-content: flex-end;
}
.app-cta__icon_arrow-right-16 {
transform: rotate(-180deg);
}
@include mq($until: medium) {
width: 100%;
.utility-panel-header {
border-bottom: 1px solid $border-color;
}
}
}
}
</style>