mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 21:22:43 +00:00
docs: add companies frontmatter, schema, and rendering in badges & appendix pages
This commit is contained in:
1 parent
c753937852
commit
f6c580b14b
109 files changed
+1218
-12
No files matched your search
@@ -7,8 +7,12 @@
|
||||
* shows (the term's icon in the tag's colour from `lib/tag-colors.ts`, over
|
||||
* the same tag universe `ProblemBadges.astro` colours) and lists the problem
|
||||
* pages whose badge links here: the backlinks a per-term page used to have.
|
||||
* The filter is a custom element over static markup, the way the Carousel
|
||||
* is built; with no JS the page is the full glossary.
|
||||
* After the terms, a Companies section: one card per company any problem
|
||||
* page's `companies:` names (`companies.ts`), the target of that company's
|
||||
* pill in the page's company table, listing the solved problems it asked in
|
||||
* the last six months and how recently. The filter is a custom element over
|
||||
* static markup, the way the Carousel is built; with no JS the page is the
|
||||
* full glossary.
|
||||
*/
|
||||
import { getEntry } from "astro:content";
|
||||
import data from "blume:data";
|
||||
@@ -16,6 +20,7 @@ import Icon from "blume/components/Icon.astro";
|
||||
import { withBase } from "blume/components/islands/base-path.ts";
|
||||
import PageLayout from "blume/components/layout/PageLayout.astro";
|
||||
import { APPENDIX } from "../appendix.ts";
|
||||
import { RECENCY, RECENCY_LABEL, companyHref, type Company } from "../companies.ts";
|
||||
import { tagColors } from "../lib/tag-colors.ts";
|
||||
import { TOPIC_LANDINGS, tagTarget, type TopicTag } from "../topic-landings.ts";
|
||||
|
||||
@@ -35,6 +40,7 @@ const problems = await Promise.all(
|
||||
name: name!,
|
||||
difficulty: entry?.data.sidebar?.badge as string | undefined,
|
||||
tags: (entry?.data.topics ?? []) as TopicTag[],
|
||||
companies: (entry?.data.companies ?? []) as Company[],
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -55,6 +61,21 @@ const terms = Object.entries(APPENDIX)
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
// Every company a problem page names, A–Z; its solved problems most recent
|
||||
// window first, then by number.
|
||||
const companies = [
|
||||
...Map.groupBy(
|
||||
problems.flatMap((p) => p.companies.map((c) => ({ ...p, company: c.name, period: c.period }))),
|
||||
(row) => row.company,
|
||||
),
|
||||
]
|
||||
.map(([name, rows]) => ({
|
||||
name,
|
||||
id: companyHref(name).slice("/appendix#".length),
|
||||
problems: rows.sort((a, b) => RECENCY.indexOf(a.period) - RECENCY.indexOf(b.period) || a.num - b.num),
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const sections = new Map<string, typeof terms>();
|
||||
for (const term of terms) {
|
||||
const letter = term.name[0]!.toUpperCase();
|
||||
@@ -73,7 +94,7 @@ const DIFFICULTY: Record<string, { label: string; class: string }> = {
|
||||
Hard: { label: "Hard", class: "border-chart-3/40 bg-chart-3/10 text-chart-3" },
|
||||
};
|
||||
const description =
|
||||
"The LeetCode topics the campaign meets without teaching them as a topic: what each one means, and the solved problems that use it.";
|
||||
"The LeetCode topics the campaign meets without teaching them as a topic, what each one means and the solved problems that use it, then the companies that asked those problems in the last six months.";
|
||||
---
|
||||
|
||||
<PageLayout
|
||||
@@ -115,6 +136,17 @@ const description =
|
||||
),
|
||||
)
|
||||
}
|
||||
{
|
||||
companies.length > 0 && (
|
||||
<a
|
||||
href="#companies"
|
||||
data-letter-link="companies"
|
||||
class="inline-flex h-7 items-center justify-center rounded-md px-2 text-foreground no-underline transition-colors hover:bg-muted"
|
||||
>
|
||||
Companies
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</nav>
|
||||
<label class="relative block sm:w-64">
|
||||
<span class="sr-only">Filter terms</span>
|
||||
@@ -125,7 +157,7 @@ const description =
|
||||
/>
|
||||
<input
|
||||
type="search"
|
||||
placeholder={`Filter ${terms.length} terms`}
|
||||
placeholder={`Filter ${terms.length} terms, ${companies.length} companies`}
|
||||
class="w-full rounded-md border border-border bg-background py-1.5 pr-3 pl-8 text-foreground text-sm outline-none transition-colors placeholder:text-muted-foreground focus:border-foreground/40"
|
||||
/>
|
||||
</label>
|
||||
@@ -231,7 +263,85 @@ const description =
|
||||
</section>
|
||||
))
|
||||
}
|
||||
<p data-empty hidden class="mt-10 text-muted-foreground">No term matches that filter.</p>
|
||||
{
|
||||
companies.length > 0 && (
|
||||
<section id="companies" data-letter="companies" class="scroll-mt-24 pt-10">
|
||||
<h2 class="m-0 text-2xl font-semibold text-foreground">Companies</h2>
|
||||
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
||||
{companies.map((company) => (
|
||||
<article
|
||||
id={company.id}
|
||||
data-term={`${company.name} company`.toLowerCase()}
|
||||
class="flex scroll-mt-24 flex-col rounded-lg border border-border bg-muted/30 p-5 transition-colors target:border-foreground/40 target:bg-muted/60"
|
||||
>
|
||||
<header class="flex items-start gap-3">
|
||||
<span class="flex size-9 shrink-0 items-center justify-center rounded-md border border-border text-muted-foreground">
|
||||
<Icon name="building-2" size={18} />
|
||||
</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<h3 class="m-0 text-base font-semibold leading-snug">
|
||||
<a class="text-foreground no-underline hover:underline" href={`#${company.id}`}>
|
||||
{company.name}
|
||||
</a>
|
||||
</h3>
|
||||
<p class="m-0 mt-0.5 font-mono text-[11px] uppercase tracking-wide text-muted-foreground">
|
||||
Company · {company.problems.length} solved
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
<solved-pages class="mt-4 block border-border border-t pt-3">
|
||||
<ul class="m-0 list-none p-0 text-sm">
|
||||
{company.problems.map((p, i) => (
|
||||
<li data-page={Math.floor(i / PAGE_SIZE)}>
|
||||
<a
|
||||
class="-mx-2 flex items-baseline gap-3 rounded-md px-2 py-1 text-foreground no-underline transition-colors hover:bg-muted"
|
||||
href={withBase(p.route)}
|
||||
>
|
||||
<span class="w-10 shrink-0 text-right font-mono text-muted-foreground text-xs tabular-nums">
|
||||
{p.num}
|
||||
</span>
|
||||
<span class="min-w-0 flex-1 truncate">{p.name}</span>
|
||||
<span class="shrink-0 text-muted-foreground text-xs">within {RECENCY_LABEL[p.period]}</span>
|
||||
{p.difficulty && DIFFICULTY[p.difficulty] && (
|
||||
<span
|
||||
class:list={[
|
||||
"shrink-0 rounded-full border px-2 py-px font-mono text-[11px] leading-4",
|
||||
DIFFICULTY[p.difficulty]!.class,
|
||||
]}
|
||||
>
|
||||
{DIFFICULTY[p.difficulty]!.label}
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{company.problems.length > PAGE_SIZE && (
|
||||
<nav
|
||||
aria-label={`${company.name} solved pages`}
|
||||
class="mt-2 flex flex-wrap justify-center gap-1 font-mono text-xs"
|
||||
hidden
|
||||
>
|
||||
{Array.from({ length: Math.ceil(company.problems.length / PAGE_SIZE) }, (_, page) => (
|
||||
<button
|
||||
type="button"
|
||||
data-goto={page}
|
||||
aria-label={`Page ${page + 1}`}
|
||||
class="inline-flex size-7 cursor-pointer items-center justify-center rounded-md border border-transparent text-muted-foreground tabular-nums transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:border-border aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
|
||||
>
|
||||
{page + 1}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</solved-pages>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
<p data-empty hidden class="mt-10 text-muted-foreground">Nothing matches that filter.</p>
|
||||
</appendix-filter>
|
||||
</section>
|
||||
</PageLayout>
|
||||
|
||||
Reference in new issue
Block a user