151 lines
4.1 KiB
Vue
151 lines
4.1 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* @file CategorySidebar.vue
|
|
* @description Sidebar for filtering courses by category
|
|
*/
|
|
|
|
const props = defineProps<{
|
|
categories: any[];
|
|
modelValue: number[]; // selectedCategoryIds
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: "update:modelValue", value: number[]): void;
|
|
}>();
|
|
|
|
const { locale, t } = useI18n();
|
|
const showAllCategories = ref(false);
|
|
|
|
const getLocalizedText = (text: any) => {
|
|
if (!text) return "";
|
|
if (typeof text === "string") return text;
|
|
|
|
const currentLocale = locale.value as 'th' | 'en';
|
|
return text[currentLocale] || text.th || text.en || "";
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="category-sidebar-root border rounded-2xl overflow-hidden shadow-sm">
|
|
<q-expansion-item
|
|
expand-separator
|
|
:label="`${$t('discovery.categoryTitle')} (${modelValue.length})`"
|
|
class="category-sidebar-expansion"
|
|
header-class="category-sidebar-header"
|
|
default-opened
|
|
>
|
|
<q-list class="category-sidebar-list border-t">
|
|
<q-item
|
|
v-for="cat in showAllCategories ? categories : categories.slice(0, 4)"
|
|
:key="cat.id"
|
|
clickable
|
|
v-ripple
|
|
dense
|
|
class="category-item"
|
|
>
|
|
<q-item-section avatar>
|
|
<q-checkbox
|
|
:model-value="modelValue"
|
|
@update:model-value="(val) => emit('update:modelValue', val)"
|
|
:val="cat.id"
|
|
color="primary"
|
|
dense
|
|
class="checkbox-visible"
|
|
/>
|
|
</q-item-section>
|
|
<q-item-section>
|
|
<q-item-label class="category-item-label text-sm font-medium">
|
|
{{ getLocalizedText(cat.name) }}
|
|
</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
|
|
<!-- Show More/Less Button -->
|
|
<q-item
|
|
v-if="categories.length > 4"
|
|
clickable
|
|
v-ripple
|
|
@click="showAllCategories = !showAllCategories"
|
|
class="show-more-item font-bold text-sm"
|
|
>
|
|
<q-item-section>
|
|
<div class="flex items-center gap-1 text-blue-600 dark:text-blue-400">
|
|
{{ showAllCategories ? $t('discovery.showLess') : $t('discovery.showMore') }}
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
:class="showAllCategories ? 'rotate-180' : ''"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M19 9l-7 7-7-7"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-expansion-item>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* Base Styles - Rely on main.css variables */
|
|
.category-sidebar-root {
|
|
background-color: var(--bg-surface);
|
|
border-color: var(--border-color);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
/* Internal Quasar components color management */
|
|
:deep(.category-sidebar-header),
|
|
.category-sidebar-list {
|
|
background-color: var(--bg-surface) !important;
|
|
color: var(--text-main) !important;
|
|
}
|
|
|
|
/* Labels and Icons - use var(--text-main) but force opacity */
|
|
:deep(.q-item__label),
|
|
:deep(.q-icon) {
|
|
color: var(--text-main) !important;
|
|
opacity: 1 !important;
|
|
}
|
|
|
|
.category-item-label {
|
|
color: var(--text-main);
|
|
}
|
|
|
|
/* ✅ DARK MODE SPECIFIC OVERRIDES using :global(.dark) as recommended */
|
|
:global(.dark) .category-item-label {
|
|
color: #f8fafc !important; /* Forces slate-50 in dark mode */
|
|
}
|
|
|
|
:global(.dark) :deep(.category-sidebar-header *) {
|
|
color: #f8fafc !important;
|
|
opacity: 1 !important;
|
|
}
|
|
|
|
/* Hover effects */
|
|
.category-item:hover {
|
|
background-color: rgba(0, 0, 0, 0.03);
|
|
}
|
|
|
|
:global(.dark) .category-item:hover {
|
|
background-color: rgba(255, 255, 255, 0.05) !important;
|
|
}
|
|
|
|
/* Checkbox Label handling */
|
|
:deep(.q-checkbox__label) {
|
|
color: var(--text-secondary) !important;
|
|
}
|
|
|
|
/* Show More button fix */
|
|
.show-more-item {
|
|
background-color: transparent !important;
|
|
}
|
|
</style>
|