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>