Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c468d0a
Basic addition to upate the side filter panel
Prashant-thakur77 Nov 8, 2025
0b61a60
added ktextbox
Prashant-thakur77 Nov 14, 2025
f408184
added kimg
Prashant-thakur77 Nov 14, 2025
16d13da
made new compnent
Prashant-thakur77 Nov 14, 2025
ee03f49
made new compnent
Prashant-thakur77 Nov 14, 2025
fca8fb0
updated catalog filters
Prashant-thakur77 Nov 14, 2025
e12e747
updated catalog filters
Prashant-thakur77 Nov 14, 2025
c1401a2
updated catalog filters
Prashant-thakur77 Nov 14, 2025
d22f575
updated catalog filters
Prashant-thakur77 Nov 14, 2025
9d496a1
updated styles
Prashant-thakur77 Nov 15, 2025
ed400e7
updated styles
Prashant-thakur77 Nov 15, 2025
d5e760a
done
Prashant-thakur77 Nov 15, 2025
33fb8b4
done
Prashant-thakur77 Nov 16, 2025
905c9d0
updated styles
Prashant-thakur77 Nov 16, 2025
349e963
Preserved the rtl support
Prashant-thakur77 Nov 16, 2025
783275b
fine tuning styling
Prashant-thakur77 Nov 16, 2025
472ed4c
added styles
Prashant-thakur77 Nov 16, 2025
046ee25
removed z-index
Prashant-thakur77 Nov 16, 2025
95ffa9c
done
Prashant-thakur77 Nov 16, 2025
8c0c628
Scrolling fixes
Prashant-thakur77 Nov 16, 2025
049383c
Updated tests
Prashant-thakur77 Nov 16, 2025
623e582
Preserved previous flat button
Prashant-thakur77 Nov 17, 2025
68369af
Made test file for catalogfilterpanelcontent
Prashant-thakur77 Nov 17, 2025
1e37e8e
Updated padding
Prashant-thakur77 Nov 19, 2025
d3edaa5
Updated search button to filter button
Prashant-thakur77 Nov 28, 2025
470b5c4
Removed media query
Prashant-thakur77 Nov 28, 2025
17d0758
Updated styles
Prashant-thakur77 Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,233 +1,89 @@
<template>

<div>
<div class="catalog-filters-wrapper">
<!-- Mobile: Filter button that opens SidePanelModal -->
<KButton
v-if="$vuetify.breakpoint.xsOnly"
class="drawer-btn"
:text="$tr('searchText')"
:primary="true"
appearance="flat-button"
@click.stop="drawer = true"
v-if="isMobile"
class="filter-button-mobile"
:text="$tr('filterText')"
appearance="raised-button"
icon="filter"
@click="openSidePanel"
/>
<CatalogFilterBar />
<VNavigationDrawer
v-model="drawer"
:permanent="$vuetify.breakpoint.smAndUp"
app
disable-route-watcher
:clipped="$vuetify.breakpoint.smAndUp"
:right="isRTL"
>
<VContainer class="filters pa-3">
<VToolbar
v-if="$vuetify.breakpoint.xsOnly"
color="transparent"
flat
dense
>
<VSpacer />
<VBtn
icon
flat
style="text-align: right"
@click="drawer = false"
>
<Icon icon="clear" />
</VBtn>
</VToolbar>

<!-- Keyword search -->
<VTextField
v-model="keywordInput"
color="primary"
:label="$tr('searchLabel')"
box
clearable
data-test="keywords"
autofocus
@input="setKeywords"
/>

<!-- Language -->
<LanguageFilter
v-model="languages"
:menu-props="menuProps"
/>

<!-- License (attach to self to keep in notranslate class) -->
<MultiSelect
v-if="!libraryMode"
v-model="licenses"
:items="licenseOptions"
:label="$tr('licenseLabel')"
/>

<!-- Formats (attach to self to keep in notranslate class) -->
<MultiSelect
v-model="kinds"
:items="kindOptions"
:label="$tr('formatLabel')"
/>

<!-- Starred -->
<Checkbox
v-if="loggedIn"
v-model="bookmark"
:label="$tr('starredLabel')"
/>

<!-- Includes -->
<div class="subheading">
{{ $tr('includesLabel') }}
</div>

<div :style="{ display: 'flex', alignItems: 'center' }">
<Checkbox
v-model="coach"
aria-describedby="tooltip-coach"
:label="$tr('coachLabel')"
/>
<HelpTooltip
:text="$tr('coachDescription')"
maxWidth="250px"
tooltipId="tooltip-coach"
/>
</div>
<!-- Desktop/Tablet: Permanent side panel as page section -->
<aside
v-if="!isMobile"
class="filter-panel-desktop"
Comment on lines +16 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, We don't usually make differentiations between "mobile vs desktop", and use instead wording like "windowIsSmall" because a Mobile device could render a "windowIsLarge" layout if we rotate it and see the content in landscape mode, and a desktop device could render a "windowIsSmall" layout if the window is shrinked or the user display is splitted displaying two or more apps.

So, it'd be great if we could call all these properties in terms of windowIsSmall/windowIsMedium/windowIsLarge naming that are the ones exposed by the useKResponsiveWindow composable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this aside node, we should also set these styles:

  • Background color: $themeTokens.surface
  • Border: $themeTokens.fineLine

:class="{ 'filter-panel-rtl': isRTL }"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like there isn't a css class called filter-panel-rtl

>
<CatalogFilterPanelContent />
</aside>

<Checkbox
v-model="subtitles"
:label="$tr('subtitlesLabel')"
/>
<KRouterLink
class="qa-link"
:to="faqLink"
:text="$tr('frequentlyAskedQuestionsLink')"
appearance="basic-link"
iconAfter="openNewTab"
target="_blank"
/>
</VContainer>
<VFooter
class="pb-3 pt-2 px-4"
color="transparent"
height="100"
>
<div>
<VImg
height="60"
width="90"
class="mb-1 mr-2"
contain
:src="require('shared/images/le-logo.svg')"
/>
<KExternalLink
href="https://learningequality.org/"
:text="$tr('copyright', { year: new Date().getFullYear() })"
openInNewTab
/>
</div>
</VFooter>
</VNavigationDrawer>
<!-- Main content area that includes CatalogFilterBar and the list -->
<div
class="main-content-area"
:class="{ 'with-sidebar': !isMobile }"
>
<CatalogFilterBar />
<slot></slot>
</div>
Comment on lines +23 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid using CatalogFilters as a wrapper component that is responsible for rendering the entire page container. It seems the primary motivation is to set the content area width to width: calc(100% - 335px), but this imposes a very specific, rigid width, which is harder to maintain in the future if any spec changes.

To make this layout more flexible, we can use a parent component for the side panel and the main content with display: flex and flex-direction: row, and set the side panel to a width of 300px, leaving the remaining width for the main content. This way, if in the future we want to change the side panel width, we can override the 300px width, making this layout even flexible enough to support a resizable side panel that takes advantage of the flex machinery.


<!-- Mobile: SidePanelModal for filters (full width) -->
<SidePanelModal
v-if="isMobile && showMobilePanel"
alignment="left"
fullscreen
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the SidePanelModal does not have a fullscreen prop

@closePanel="closeSidePanel"
>
<CatalogFilterPanelContent />
</SidePanelModal>
</div>

</template>


<script>
import { mapGetters } from 'vuex';
import debounce from 'lodash/debounce';
import { RouteNames } from '../../constants';
import useKResponsiveWindow from 'kolibri-design-system/lib/composables/useKResponsiveWindow';
import CatalogFilterBar from './CatalogFilterBar';
import { catalogFilterMixin } from './mixins';
import LanguageFilter from './components/LanguageFilter';
import MultiSelect from 'shared/views/form/MultiSelect';
import { constantsTranslationMixin } from 'shared/mixins';
import Checkbox from 'shared/views/form/Checkbox';
import HelpTooltip from 'shared/views/HelpTooltip';
import { ContentKindsNames } from 'shared/leUtils/ContentKinds';
const excludedKinds = new Set([ContentKindsNames.TOPIC, ContentKindsNames.H5P]);
import CatalogFilterPanelContent from './components/CatalogFilterPanelContent.vue';
import SidePanelModal from 'shared/views/SidePanelModal';
export default {
name: 'CatalogFilters',
components: {
LanguageFilter,
Checkbox,
HelpTooltip,
MultiSelect,
CatalogFilterBar,
CatalogFilterPanelContent,
SidePanelModal,
},
setup() {
const { windowIsSmall } = useKResponsiveWindow();
return {
isMobile: windowIsSmall,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem, let's just return the windowIsSmall property as is.

};
},
mixins: [constantsTranslationMixin, catalogFilterMixin],
data() {
return {
drawer: false,
keywordInput: '',
showMobilePanel: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem, this shouldn't be necessarily related to a "mobile" device.

};
},
computed: {
...mapGetters(['loggedIn']),
isRTL() {
return window.isRTL;
},
libraryMode() {
return window.libraryMode;
},
faqLink() {
return { name: RouteNames.CATALOG_FAQ };
},
menuProps() {
return { offsetY: true, maxHeight: 270 };
},
kindOptions() {
return (window.publicKinds || [])
.map(kind => {
if (!excludedKinds.has(kind)) {
return {
value: kind,
text: this.translateConstant(kind),
};
}
})
.filter(Boolean);
},
licenseOptions() {
return (window.publicLicenses || []).map(id => {
return {
value: Number(id),
text: this.translateLicense(Number(id)),
};
});
},
setKeywords() {
return debounce(this.updateKeywords, 500);
},
},
watch: {
keywords() {
this.keywordInput = this.keywords;
},
},
beforeMount() {
this.keywordInput = this.$route.query.keywords;
},
methods: {
updateKeywords() {
this.keywords = this.keywordInput;
openSidePanel() {
this.showMobilePanel = true;
},
closeSidePanel() {
this.showMobilePanel = false;
},
},
$trs: {
searchLabel: 'Keywords',
coachLabel: 'Resources for coaches',
subtitlesLabel: 'Captions or subtitles',
starredLabel: 'Starred',
licenseLabel: 'Licenses',
formatLabel: 'Formats',
includesLabel: 'Display only channels with',
searchText: 'Search',
coachDescription: 'Resources for coaches are only visible to coaches in Kolibri',
frequentlyAskedQuestionsLink: 'Frequently asked questions',
copyright: '© {year} Learning Equality',
filterText: 'Filter',
},
};
Expand All @@ -236,33 +92,34 @@

<style lang="scss" scoped>
.v-input--checkbox {
margin: 0;
}
::v-deep .v-messages {
display: none;
.catalog-filters-wrapper {
display: flex;
flex-direction: column;
width: 100%;
overflow-x: hidden;
}
.subheading {
margin-top: 20px;
margin-bottom: 5px;
font-weight: bold;
color: gray;
.filter-button-mobile {
align-self: flex-start;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is setting the align-self: flex-start really needed? 👀

margin: 16px;
}
.filters {
width: 100%;
height: calc(100% - 100px);
overflow: auto;
.filter-panel-desktop {
position: fixed;
top: 100px;
left: 0;
width: 335px;
overflow-y: auto;
Comment on lines +107 to +112
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually try to avoid position: fixed layouts unless really necessary. As per my previous comments, we can deal with this layout without it.

Also, the original width of the side panel is 300px, let's keep that width after the refactor!

}
.qa-link {
margin-top: 24px;
}
.main-content-area {
flex: 1;
min-height: 100vh;
.drawer-btn {
margin-top: 10px;
&.with-sidebar {
width: calc(100% - 335px);
margin-left: 335px;
}
}
</style>
Loading