added a new component for breadcrumbs and reworked the component for getting icons using sprites
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
<a :class="{'text-white font-normal bg-primary border': isSameRoute(page.path), 'text-gray-600 hover:text-[#2C6288]': !isSameRoute(page.path) }"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'"
|
||||
class="duration-300 flex gap-x-2 w-full rounded-md cursor-pointer items-center px-2 py-1.5 text-left text-sm">
|
||||
<BasicIcon v-if="page.icon" class="w-4" :name="page.icon" />
|
||||
<BasicIcon v-if="page.icon" class="w-4 h-4" :name="page.icon" />
|
||||
{{ page.title }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<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">
|
||||
<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 v-if="breadcrumbs">
|
||||
@@ -53,7 +53,7 @@
|
||||
{{ textLimit(postTitle, 60) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</BreadcrumbsWrapper >
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-primary-hover" href="/">
|
||||
@@ -203,7 +203,8 @@ export default {
|
||||
|
||||
|
||||
<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,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,
|
||||
|
||||
@@ -1,45 +1,47 @@
|
||||
<template>
|
||||
<svg v-bind="svgAttributes" v-html="icon.path"></svg>
|
||||
<svg aria-hidden="true">
|
||||
<use :href="symbolId" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icons from "@/assets/icons/icons.js"
|
||||
export default {
|
||||
name: "BasicIcon",
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
viewBox: {
|
||||
type: String,
|
||||
},
|
||||
stroke_width: {
|
||||
type: Number,
|
||||
},
|
||||
fill: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
icon() {
|
||||
return icons[this.transformIconName(this.name)] || icons['file'];
|
||||
},
|
||||
svgAttributes() {
|
||||
return {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
fill: this.icon.fill || 'none',
|
||||
viewBox: this.icon.viewBox || this.viewBox || '0 0 24 24',
|
||||
'stroke-width': this.icon.stroke_width || this.stroke_width || 1.5,
|
||||
stroke: this.icon.stroke || 'currentColor',
|
||||
class: this.$attrs.class || 'w-6 h-6'
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transformIconName(iconName) {
|
||||
return iconName.replace(/^heroicon-(o|c|s|m)-/, '')
|
||||
.replace(/-(\w)/g, (_, letter) => letter.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
import { defineComponent, computed } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: "BasicIcon",
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const rawName = props.name.replace(/^heroicon-(o|c|s|m)-/, '');
|
||||
const symbolId = computed(() => `#${props.prefix}-${rawName}`)
|
||||
return { symbolId }
|
||||
},
|
||||
// computed: {
|
||||
// icon() {
|
||||
// return icons[this.transformIconName(this.name)] || icons['file'];
|
||||
// },
|
||||
// svgAttributes() {
|
||||
// return {
|
||||
// xmlns: "http://www.w3.org/2000/svg",
|
||||
// fill: this.icon.fill || 'none',
|
||||
// viewBox: this.icon.viewBox || this.viewBox || '0 0 24 24',
|
||||
// 'stroke-width': this.icon.stroke_width || this.stroke_width || 1.5,
|
||||
// stroke: this.icon.stroke || 'currentColor',
|
||||
// class: this.$attrs.class || 'w-6 h-6'
|
||||
// };
|
||||
// }
|
||||
// },
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user