added a new component for breadcrumbs and reworked the component for getting icons using sprites

This commit is contained in:
F4ilji
2025-05-15 16:18:39 +05:00
parent f1fd06beb3
commit ace682126e
37 changed files with 6420 additions and 544 deletions
@@ -0,0 +1,142 @@
<template>
<div class="flex justify-between pb-4 items-center">
<div class="flex w-full sm:items-center gap-x-5 sm:gap-x-3 truncate mask-fade">
<div class="grow">
<div class="grid sm:flex sm:justify-between sm:items-center gap-2">
<BreadcrumbsWrapper>
<li class="text-sm">
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-primary-hover" href="/">
<BasicIcon class="size-5" name="home" />
</Link>
</li>
<li v-if="breadcrumbs?.mainSection?.data?.title" class="text-sm">
<span class="flex items-center text-gray-500 hover:text-primary-hover cursor-pointer" @click.prevent="handleSectionClick(breadcrumbs.mainSection)">
<BasicIcon class="flex-shrink-0 mx-2 overflow-visible h-2.5 w-2.5 text-gray-400" name="breadcrumb-tag" />
{{ TEXT_LIMIT(breadcrumbs.mainSection.data.title, 25) }}
</span>
</li>
<li v-if="breadcrumbs?.subSection?.data?.title" class="text-sm">
<span class="flex items-center text-gray-500 hover:text-primary-hover cursor-pointer" @click.prevent="handleSubSectionClick(breadcrumbs.mainSection, breadcrumbs.subSection)">
<BasicIcon class="flex-shrink-0 mx-2 overflow-visible h-2.5 w-2.5 text-gray-400" name="breadcrumb-tag" />
{{ TEXT_LIMIT(breadcrumbs.subSection.data.title, 25) }}
</span>
</li>
<slot />
</BreadcrumbsWrapper>
</div>
</div>
</div>
</div>
</template>
<script>
import {Link} from "@inertiajs/vue3";
import slugify from "slugify";
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
import BreadcrumbsItem from "@/componentss/shared/Breadcrumbs/BreadcrumbsItem.vue";
import {helpers} from "@/mixins/Helpers.js";
export default {
mixins: [helpers],
name: "BaseBreadcrumbs",
components: {BreadcrumbsItem, BreadcrumbsWrapper, BasicIcon, Link},
data() {
return {
}
},
methods: {
isMobileDevice() {
if (typeof window !== 'undefined') {
return window.innerWidth < 1024; // Проверка на мобильные устройства
}
return false; // По умолчанию возвращаем false, когда не в браузере
},
handleSectionClick(breadcrumb) {
if (this.isMobileDevice()) {
this.toggleMobileNavSection(breadcrumb)
} else {
this.toggleDesktopNavSection(breadcrumb)
}
},
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
if (this.isMobileDevice()) {
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
} else {
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
}
},
highlightNavItem(item) {
item.classList.add('animate-pulse');
setTimeout(() => {
item.classList.remove('animate-pulse');
}, 4000);
},
openMobileNavMenu() {
const openMobileNavBtn = document.getElementById('open-mobile-btn');
openMobileNavBtn.click();
},
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
this.openMobileNavMenu()
const mobileNavElement = document.getElementById('open-mobile-nav');
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
if (!sectionNavBlock.classList.contains('active')) {
sectionNavBlockBtn.click()
}
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
if (subSectionNavBlock.classList.contains('active')) {
subSectionNavBlockBtn.click()
}
this.highlightNavItem(subSectionNavBlock)
},
toggleMobileNavSection(breadcrumb) {
const mobileNavElement = document.getElementById('open-mobile-nav');
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
if (sectionNavBlock.classList.contains('active')) {
sectionNavBlockBtn.click()
}
this.openMobileNavMenu()
this.highlightNavItem(sectionNavBlock)
},
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
const desktopNavElement = document.getElementById('desktop-nav');
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
sectionNavBlockBtn.click()
this.highlightNavItem(sectionNavTitle)
},
toggleDesktopNavSection(breadcrumb) {
const desktopNavElement = document.getElementById('desktop-nav');
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
sectionNavBlockBtn.click()
// this.highlightNavItem(sectionNavBlock)
},
},
props: {
breadcrumbs: {
type: Object,
},
},
}
</script>
<style scoped>
.mask-fade {
mask-image: linear-gradient(to right, black 90%, transparent 100%);
-webkit-mask-image: linear-gradient(to right, black 90%, transparent 100%);
}
</style>
@@ -0,0 +1,36 @@
<template>
<li class="text-sm">
<Link :href="url" class="flex items-center text-gray-500 hover:text-primary-light">
<BasicIcon v-if="separator" class="flex-shrink-0 mx-2 overflow-visible h-2.5 w-2.5 text-gray-400" name="breadcrumb-tag" />
{{ title }}
</Link>
</li>
</template>
<script>
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
import {Link} from "@inertiajs/vue3";
export default {
name: "BreadcrumbsItem",
props: {
title: {
type: String,
required: true
},
url: {
type: String,
// default: this.$page.url
},
separator: {
type: Boolean,
default: true
},
},
components: {
BasicIcon,
Link,
},
}
</script>
@@ -26,8 +26,10 @@
<a v-for="item in resource.data.content" class="group flex-shrink-0 w-64 flex flex-col bg-white border shadow-sm rounded-xl focus:outline-none focus:shadow-md transition" :href="item.link">
<div class="aspect-w-16 aspect-h-9 p-3 rounded-xl m-1 group-hover:bg-gray-100">
<img v-if="item.image" class="w-full backdrop-blur-xl rounded-xl object-cover h-[150px]" :src="'/storage/' + item.image" alt="Blog Image">
<div v-else :class="randomBgClass()" class="w-full rounded-xl object-cover h-[150px] bg-gradient-to-tr" />
<img v-if="!item.image || item.image.length > 0" class="w-full backdrop-blur-xl rounded-xl object-cover h-[150px]" :src="'/storage/' + item.image" alt="Blog Image">
<div v-else class="w-full rounded-xl object-cover h-[150px] flex justify-center items-center">
<BasicIcon class="w-10 h-10 text-gray-500" :name="item.icon" />
</div>
</div>
<div class="px-5 pb-4">
<h3 class="mt-2 truncate text-base font-medium text-gray-800 group-hover:text-primary-hover">{{ item.title }}</h3>
@@ -51,9 +53,10 @@
import axios from "axios";
import {Link} from "@inertiajs/vue3";
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
export default {
name: "PageResourceList",
components: {axios, Link },
components: {BasicIcon, axios, Link },
data() {
return {
resource: null,