separate vue logic into different components

This commit is contained in:
Julie Lenaerts 2023-11-20 15:50:02 +01:00
parent af3d06e7d3
commit 13c33567fd
3 changed files with 138 additions and 103 deletions

View File

@ -161,7 +161,7 @@ export interface LocationType {
title: TranslatableString;
}
export interface NewsItem {
export interface NewsItemType {
id: number;
title: string;
content: string;

View File

@ -2,27 +2,7 @@
<div>
<h1>{{ $t('widget.news.title') }}</h1>
<ul class="scrollable">
<li v-for="item in newsItems" :key="item.id">
<h2>{{ item.title }}</h2>
<div class="content" v-if="shouldTruncate(item.content)">
<div v-html="prepareContent(item.content)"></div>
<span class="read-more" @click="() => openModal(item)">{{ $t('widget.news.readMore') }}</span>
</div>
<div class="content" v-else>
<div v-html="convertMarkdownToHtml(item.content)"></div>
</div>
<modal v-if="showModal" @close="closeModal">
<template #header>
<p class="news-title">{{ item.title }}</p>
</template>
<template #body>
<p class="news-date">
{{ $t('widget.news.date') }}: <span>{{ formatDate(item.startdate.date) }}</span>
</p>
<div v-html="convertMarkdownToHtml(item.content)"></div>
</template>
</modal>
</li>
<NewsItem v-for="item in newsItems" :item="item" :key="item.id" />
</ul>
</div>
</template>
@ -30,54 +10,15 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { makeFetch } from '../../../lib/api/apiMethods';
import Modal from '../../_components/Modal.vue'; // Adjust the import path
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import {DateTime, NewsItem} from '../../../types';
import Modal from '../../_components/Modal.vue';
import { NewsItemType } from '../../../types';
import NewsItem from './NewsItem.vue';
const newsItems = ref<NewsItem[]>([])
const selectedArticle = ref<NewsItem | null>(null);
const showModal = ref(false);
const openModal = (item: NewsItem) => {
selectedArticle.value = item;
showModal.value = true;
};
const closeModal = () => {
selectedArticle.value = null;
showModal.value = false;
};;
const shouldTruncate = (content: string, maxLength = 100): boolean => {
return content.length > maxLength;
};
const truncateContent = (content: string, maxLength = 100): string => {
if (shouldTruncate(content, maxLength)) {
return content.slice(0, maxLength) + '...';
} else {
return content;
}
};
const convertMarkdownToHtml = (markdown: string): string => {
const rawHtml = marked(markdown);
return DOMPurify.sanitize(rawHtml)
};
const prepareContent = (content: string, maxLength = 100): string => {
const truncatedContent = truncateContent(content, maxLength);
return convertMarkdownToHtml(truncatedContent);
};
const formatDate = (datetime: DateTime): string => {
return new Date(datetime.toString()).toLocaleDateString('fr-FR')
}
const newsItems = ref<NewsItemType[]>([])
onMounted(() => {
makeFetch('GET', '/api/1.0/main/news.json')
.then((response: { results: NewsItem[] }) => {
.then((response: { results: NewsItemType[] }) => {
// console.log('news articles', response.results)
newsItems.value = response.results
})
@ -94,46 +35,13 @@ ul {
padding: 0;
}
li {
margin-bottom: 20px;
overflow: hidden;
}
h2 {
margin-bottom: 10px;
}
.content {
max-height: 100px; /* Adjust the max height as needed */
overflow: hidden;
}
/*
.scrollable {
overflow-y: auto;
max-height: 150px; /* Same as .content max-height */
margin-bottom: 10px; /* To give space for the scrollbar */
max-height: 150px; !* Same as .content max-height *!
margin-bottom: 10px; !* To give space for the scrollbar *!
}
*/
button {
cursor: pointer;
background-color: #3498db;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 3px;
}
.read-more {
color: #3498db; /* Adjust the color as needed */
cursor: pointer;
}
.news-date {
font-style: italic;
}
.news-title {
font-weight: bold;
text-transform: uppercase;
}
</style>

View File

@ -0,0 +1,127 @@
<template>
<li>
<h2>{{ props.item.title }}</h2>
<div class="content" v-if="shouldTruncate(item.content)">
<div v-html="prepareContent(item.content)"></div>
<span class="read-more" @click="() => openModal(item)">{{ $t('widget.news.readMore') }}</span>
</div>
<div class="content" v-else>
<div v-html="convertMarkdownToHtml(item.content)"></div>
</div>
<modal v-if="showModal" @close="closeModal">
<template #header>
<p class="news-title">{{ item.title }}</p>
</template>
<template #body>
<p class="news-date">
{{ $t('widget.news.date') }}: <span>{{ formatDate(item.startdate.date) }}</span>
</p>
<div v-html="convertMarkdownToHtml(item.content)"></div>
</template>
</modal>
</li>
</template>
<script setup lang="ts">
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import { DateTime, NewsItemType } from "../../../types";
import type { PropType } from 'vue'
import { defineProps, ref } from "vue";
import {dateToISO} from "../../../chill/js/date";
const props = defineProps({
item: {
type: Object as PropType<NewsItemType>,
required: true
}
})
const selectedArticle = ref<NewsItemType | null>(null);
const showModal = ref(false);
const openModal = (item: NewsItemType) => {
selectedArticle.value = item;
showModal.value = true;
};
const closeModal = () => {
selectedArticle.value = null;
showModal.value = false;
};
const shouldTruncate = (content: string, maxLength = 100): boolean => {
return content.length > maxLength;
};
const truncateContent = (content: string, maxLength = 100): string => {
if (shouldTruncate(content, maxLength)) {
const lastSpaceIndex = content.lastIndexOf(' ', maxLength);
console.log(lastSpaceIndex)
if (lastSpaceIndex !== -1) {
return content.slice(0, lastSpaceIndex) + '...';
} else {
return content.slice(0, maxLength) + '...';
}
} else {
return content;
}
};
const convertMarkdownToHtml = (markdown: string): string => {
const rawHtml = marked(markdown);
return DOMPurify.sanitize(rawHtml)
};
const prepareContent = (content: string, maxLength = 100): string => {
const truncatedContent = truncateContent(content, maxLength);
return convertMarkdownToHtml(truncatedContent);
};
const formatDate = (datetime: DateTime): string|null => {
return dateToISO(new Date(datetime.toString()))
}
</script>
<style scoped>
li {
margin-bottom: 20px;
overflow: hidden;
}
h2 {
margin-bottom: 10px;
}
.content {
max-height: 100px; /* Adjust the max height as needed */
overflow: hidden;
}
button {
cursor: pointer;
background-color: #3498db;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 3px;
}
.read-more {
color: #3498db; /* Adjust the color as needed */
cursor: pointer;
}
.news-date {
font-style: italic;
}
.news-title {
font-weight: bold;
text-transform: uppercase;
}
</style>