feat: add example picker dropdown to sample diagrams

Each diagram type in the Sample Diagrams panel is now a split button:
clicking the diagram name loads its default example, and a dropdown
arrow on the right (shown when a diagram has more than one example)
opens a popover listing all examples for that diagram.

- getSampleDiagrams now returns all examples per diagram with the
  default example first, instead of flattening to the default's code
- loadSampleDiagram analytics now include the chosen example title

https://claude.ai/code/session_015rUgHChBEkquLu77fMxmQ6

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sidharth Vinod
2026-06-11 00:09:45 +05:30
co-authored by Claude Fable 5
parent 6a9a306ed7
commit a257f78d8e
3 changed files with 87 additions and 27 deletions
+47 -10
View File
@@ -1,13 +1,20 @@
<script lang="ts">
import Card from '$/components/Card/Card.svelte';
import { Button } from '$/components/ui/button';
import { getSampleDiagrams } from '$/util/mermaid';
import { Button, buttonVariants } from '$/components/ui/button';
import * as Popover from '$/components/ui/popover';
import { getSampleDiagrams, type SampleExample } from '$/util/mermaid';
import { updateCode } from '$lib/util/state.svelte';
import { logEvent } from '$lib/util/stats';
import { cn } from '$lib/utils';
import ShapesIcon from '~icons/material-symbols/account-tree-outline-rounded';
import ChevronDownIcon from '~icons/material-symbols/keyboard-arrow-down-rounded';
const extras = {
ZenUML: `zenuml
const extras: Record<string, SampleExample[]> = {
ZenUML: [
{
title: 'Order Service',
isDefault: true,
code: `zenuml
title Order Service
@Actor Client #FFEBE6
@Boundary OrderController #0747A6
@@ -31,15 +38,18 @@
}
}
`
}
]
};
const samples = { ...getSampleDiagrams(), ...extras } as const;
const loadSampleDiagram = (diagramType: string): void => {
updateCode(samples[diagramType as keyof typeof samples], {
const samples = { ...getSampleDiagrams(), ...extras };
const loadSampleDiagram = (diagramType: string, example: SampleExample): void => {
updateCode(example.code, {
resetPanZoom: true,
updateDiagram: true
});
logEvent('loadSampleDiagram', { diagramType });
logEvent('loadSampleDiagram', { diagramType, exampleTitle: example.title });
};
const mainDiagrams = [
@@ -62,12 +72,39 @@
<Card title="Sample Diagrams" isOpen isStackable icon={{ component: ShapesIcon }}>
<div class="flex h-fit max-h-52 flex-wrap gap-2 overflow-y-auto p-2">
{#each diagramOrder as sample (sample)}
{@const examples = samples[sample]}
<div class="flex min-w-20 flex-grow">
<Button
size="sm"
class="w-fit min-w-20 flex-grow normal-case"
onclick={() => loadSampleDiagram(sample)}>
class={cn('flex-grow normal-case', examples.length > 1 && 'rounded-r-none')}
onclick={() => loadSampleDiagram(sample, examples[0])}>
{sample}
</Button>
{#if examples.length > 1}
<Popover.Root>
<Popover.Trigger
aria-label="Choose a {sample} example"
class={cn(
buttonVariants({ size: 'sm' }),
'rounded-l-none border-l border-primary-foreground/30 px-0.5 [&_svg]:size-5'
)}>
<ChevronDownIcon />
</Popover.Trigger>
<Popover.Content align="start" class="flex w-fit flex-col gap-1 p-1">
{#each examples as example (example.title)}
<Popover.Close
class={cn(
buttonVariants({ variant: 'ghost', size: 'sm' }),
'justify-start normal-case'
)}
onclick={() => loadSampleDiagram(sample, example)}>
{example.title}
</Popover.Close>
{/each}
</Popover.Content>
</Popover.Root>
{/if}
</div>
{/each}
</div>
</Card>
+23
View File
@@ -0,0 +1,23 @@
import { describe, expect, it } from 'vitest';
import { getSampleDiagrams } from './mermaid';
describe('getSampleDiagrams', () => {
const samples = getSampleDiagrams();
it('should return at least one example per diagram', () => {
expect(Object.keys(samples).length).toBeGreaterThan(0);
for (const [name, examples] of Object.entries(samples)) {
expect(examples.length, `${name} should have at least one example`).toBeGreaterThan(0);
for (const example of examples) {
expect(example.title, `${name} has an example without a title`).toBeTruthy();
expect(example.code, `${name} example "${example.title}" has no code`).toBeTruthy();
}
}
});
it('should list the default example first', () => {
for (const [name, examples] of Object.entries(samples)) {
expect(examples[0].isDefault, `${name} should have its default example first`).toBe(true);
}
});
});
+11 -11
View File
@@ -49,20 +49,20 @@ export const standardizeDiagramType = (diagramType: string) => {
type DiagramDefinition = (typeof diagramData)[number];
export type SampleExample = DiagramDefinition['examples'][number];
const isValidDiagram = (diagram: DiagramDefinition): diagram is Required<DiagramDefinition> => {
return Boolean(diagram.name && diagram.examples && diagram.examples.length > 0);
};
export const getSampleDiagrams = () => {
const diagrams = diagramData
.filter((d) => isValidDiagram(d))
.map(({ examples, ...rest }) => ({
...rest,
example: examples?.filter(({ isDefault }) => isDefault)[0]
}));
const examples: Record<string, string> = {};
for (const diagram of diagrams) {
examples[diagram.name.replace(/ (Diagram|Chart|Graph)/, '')] = diagram.example.code;
export const getSampleDiagrams = (): Record<string, SampleExample[]> => {
const samples: Record<string, SampleExample[]> = {};
for (const diagram of diagramData.filter((d) => isValidDiagram(d))) {
// The default example comes first, so it is loaded when clicking the
// diagram name and shown at the top of the example dropdown.
samples[diagram.name.replace(/ (Diagram|Chart|Graph)/, '')] = [...diagram.examples].sort(
(a, b) => Number(b.isDefault ?? false) - Number(a.isDefault ?? false)
);
}
return examples;
return samples;
};