master
1---
2import { linksConfig } from '@/config';
3import PostInfo from '@components/misc/PostInfo.astro';
4import ImageWrapper from '@components/utils/ImageWrapper.astro';
5import Markdown from '@components/utils/Markdown.astro';
6import PostPageLayout from '@layouts/PostPageLayout.astro';
7import { getAllReferences } from '@utils/content-utils';
8import { t } from '@utils/i18n';
9import { getEntry, render } from 'astro:content';
10
11const md = await getEntry('spec', 'links');
12
13const { Content, headings, remarkPluginFrontmatter } = md
14 ? await render(md)
15 : {
16 Content: Fragment,
17 headings: [],
18 remarkPluginFrontmatter: { references: [] },
19 };
20
21const allReferences = await getAllReferences();
22let allRefByCurrent: typeof allReferences = [];
23let references: {
24 reference: string;
25 context: string;
26 id: string;
27}[] = [];
28if (md) {
29 allRefByCurrent = allReferences.filter((it) => it.refBy.id === md.id);
30 references = remarkPluginFrontmatter.references || [];
31}
32
33const groupHeadings = linksConfig.items.map((item) => ({
34 depth: 2,
35 slug: `links-group-${item.groupName.toLowerCase().replace(/\s/g, '-')}`,
36 text: item.groupName,
37}));
38---
39
40<PostPageLayout
41 title={t.navigation.friendLinks()}
42 comment={md?.data.comment}
43 headings={[...groupHeadings, ...headings]}
44>
45 <Fragment slot="header-content">
46 <PostInfo title={t.navigation.friendLinks()} />
47 </Fragment>
48 {
49 linksConfig.items.map((item) => (
50 <Fragment>
51 <div class="mt-4 mb-1 grid grid-cols-[auto_minmax(0,1fr)] items-baseline justify-between gap-4">
52 {' '}
53 <h2
54 class="text-2xl font-bold"
55 id={`links-group-${item.groupName.toLowerCase().replace(/\s/g, '-')}`}
56 >
57 {item.groupName}
58 </h2>
59 {item.groupDescription && (
60 <span class="text-base-content/60 line-clamp-1 text-right text-sm font-medium">
61 {item.groupDescription}
62 </span>
63 )}
64 </div>
65 <hr class="text-base-content/25 my-1" />
66 <div class="flex flex-wrap">
67 {item.groupItems.map((item) => (
68 <div class="card max-md:card-side link-card">
69 <figure class="shrink-0">
70 <a
71 href={item.url}
72 target="_blank"
73 rel="noopener noreferrer"
74 class="w-full"
75 title={item.name}
76 >
77 <ImageWrapper src={item.avatar} class="max-md:h-24 max-md:w-24 md:h-48" />
78 </a>
79 </figure>
80 <div class="card-body from-base-300 to-base-100 bg-linear-150 px-4 py-2">
81 <a
82 href={item.url}
83 target="_blank"
84 rel="noopener noreferrer"
85 class="w-fit"
86 title={item.name}
87 >
88 <h3 class="card-title line-clamp-1">{item.name}</h3>
89 </a>
90 {item.description && (
91 <p class="text-base-content/60 line-clamp-2 text-sm">{item.description}</p>
92 )}
93 </div>
94 </div>
95 ))}
96 </div>
97 </Fragment>
98 ))
99 }
100 <hr class="text-base-content/25 mt-8" />
101 <Markdown
102 bidirectional-references={md
103 ? {
104 references,
105 allRefByCurrent,
106 }
107 : undefined}
108 >
109 <Content />
110 </Markdown>
111</PostPageLayout>
112
113<style>
114 @reference '@/styles/global.css';
115
116 .link-card {
117 @apply border-base-300 m-1 overflow-hidden border;
118 @apply w-full md:w-[calc(25%-calc(var(--spacing)*2))] lg:w-[calc(20%-calc(var(--spacing)*2))];
119
120 figure {
121 @apply min-w-23;
122 }
123 }
124</style>