125 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <script setup lang="ts">
 | |
| import { IconDragDrop, IconHeart } from '@tabler/icons-vue';
 | |
| import { useHead } from '@vueuse/head';
 | |
| import { computed } from 'vue';
 | |
| import Draggable from 'vuedraggable';
 | |
| import ColoredCard from '../components/ColoredCard.vue';
 | |
| import ToolCard from '../components/ToolCard.vue';
 | |
| import { useToolStore } from '@/tools/tools.store';
 | |
| import { config } from '@/config';
 | |
| 
 | |
| const toolStore = useToolStore();
 | |
| 
 | |
| useHead({ title: 'IT Tools - Handy online tools for developers' });
 | |
| const { t } = useI18n();
 | |
| 
 | |
| const favoriteTools = computed(() => toolStore.favoriteTools);
 | |
| 
 | |
| // Update favorite tools order when drag is finished
 | |
| function onUpdateFavoriteTools() {
 | |
|   toolStore.updateFavoriteTools(favoriteTools.value); // Update the store with the new order
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <template>
 | |
|   <div class="pt-50px">
 | |
|     <div class="grid-wrapper">
 | |
|       <div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
 | |
|         <ColoredCard v-if="config.showBanner" :title="$t('home.follow.title')" :icon="IconHeart">
 | |
|           {{ $t('home.follow.p1') }}
 | |
|           <a
 | |
|             href="https://github.com/CorentinTh/it-tools"
 | |
|             rel="noopener"
 | |
|             target="_blank"
 | |
|             :aria-label="$t('home.follow.githubRepository')"
 | |
|           >GitHub</a>
 | |
|           {{ $t('home.follow.p2') }}
 | |
|           <a
 | |
|             href="https://x.com/ittoolsdottech"
 | |
|             rel="noopener"
 | |
|             target="_blank"
 | |
|             :aria-label="$t('home.follow.twitterXAccount')"
 | |
|           >X</a>.
 | |
|           {{ $t('home.follow.thankYou') }}
 | |
|           <n-icon :component="IconHeart" />
 | |
|         </ColoredCard>
 | |
|       </div>
 | |
| 
 | |
|       <transition name="height">
 | |
|         <div v-if="toolStore.favoriteTools.length > 0">
 | |
|           <h3 class="mb-5px mt-25px text-neutral-400 font-500">
 | |
|             {{ $t('home.categories.favoriteTools') }}
 | |
|             <c-tooltip :tooltip="$t('home.categories.favoritesDndToolTip')">
 | |
|               <n-icon :component="IconDragDrop" size="18" />
 | |
|             </c-tooltip>
 | |
|           </h3>
 | |
|           <Draggable
 | |
|             :list="favoriteTools"
 | |
|             class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4"
 | |
|             ghost-class="ghost-favorites-draggable"
 | |
|             item-key="name"
 | |
|             @end="onUpdateFavoriteTools"
 | |
|           >
 | |
|             <template #item="{ element: tool }">
 | |
|               <ToolCard :tool="tool" />
 | |
|             </template>
 | |
|           </Draggable>
 | |
|         </div>
 | |
|       </transition>
 | |
| 
 | |
|       <div v-if="toolStore.newTools.length > 0">
 | |
|         <h3 class="mb-5px mt-25px text-neutral-400 font-500">
 | |
|           {{ t('home.categories.newestTools') }}
 | |
|         </h3>
 | |
|         <div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
 | |
|           <ToolCard v-for="tool in toolStore.newTools" :key="tool.name" :tool="tool" />
 | |
|         </div>
 | |
|       </div>
 | |
| 
 | |
|       <h3 class="mb-5px mt-25px text-neutral-400 font-500">
 | |
|         {{ $t('home.categories.allTools') }}
 | |
|       </h3>
 | |
|       <div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
 | |
|         <ToolCard v-for="tool in toolStore.tools" :key="tool.name" :tool="tool" />
 | |
|       </div>
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <style scoped lang="less">
 | |
| .height-enter-active,
 | |
| .height-leave-active {
 | |
|   transition: all 0.5s ease-in-out;
 | |
|   overflow: hidden;
 | |
|   max-height: 500px;
 | |
| }
 | |
| 
 | |
| .height-enter-from,
 | |
| .height-leave-to {
 | |
|   max-height: 42px;
 | |
|   overflow: hidden;
 | |
|   opacity: 0;
 | |
|   margin-bottom: 0;
 | |
| }
 | |
| 
 | |
| .ghost-favorites-draggable {
 | |
|   opacity: 0.4;
 | |
|   background-color: #ccc;
 | |
|   border: 2px dashed #666;
 | |
|   box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
 | |
|   transform: scale(1.1);
 | |
|   animation: ghost-favorites-draggable-animation 0.2s ease-out;
 | |
| }
 | |
| 
 | |
| @keyframes ghost-favorites-draggable-animation {
 | |
|   0% {
 | |
|     opacity: 0;
 | |
|     transform: scale(0.9);
 | |
|   }
 | |
|   100% {
 | |
|     opacity: 0.4;
 | |
|     transform: scale(1.0);
 | |
|   }
 | |
| }
 | |
| </style>
 |