Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/platforms/community.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 9 additions & 4 deletions docs/platforms/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ We also <Link to="https://sentry.zendesk.com/hc/en-us/articles/23961784367387">d

</Note>

## Platforms

<PlatformGrid />
<PlatformFilter />

## Community Supported

These SDKs are maintained and supported by [the Sentry community](https://open.sentry.io/). While generally our community does a great job at responding to issues, it's important to understand that Sentry staff cannot help you with issues using a community-supported SDK.
<div style={{display: 'flex', flexWrap: 'wrap', gap: '0.5rem'}}>
<div style={{width: '100%', maxWidth: '50ch'}}>
These SDKs are maintained and supported by [the Sentry community](https://open.sentry.io/). While generally our community does a great job at responding to issues, it's important to understand that Sentry staff cannot help you with issues using a community-supported SDK.
</div>
<div style={{diplay: 'inline', flex: 1, minWidth: '120px', maxWidth: '250px'}}>
![Community Supported](./community.png)
</div>
</div>

- Clojure
- [_sentry-clj_](https://github.com/getsentry/sentry-clj)
Expand Down
15 changes: 2 additions & 13 deletions src/components/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Image from 'next/image';

import {Banner} from 'sentry-docs/components/banner';
import {SentryWordmarkLogo} from 'sentry-docs/components/wordmarkLogo';
import {extractPlatforms, getDocsRootNode} from 'sentry-docs/docTree';
import PlugImage from 'sentry-docs/imgs/api.png';
import ChatBubble from 'sentry-docs/imgs/chat-bubble.png';
import TerminalImage from 'sentry-docs/imgs/cli.png';
Expand All @@ -19,17 +18,7 @@ import {Header} from './header';
import {NavLink, NavLinkProps} from './navlink';
import {PlatformFilter} from './platformFilter';

export async function Home() {
const rootNode = await getDocsRootNode();
// this regex deals with names like .NET that would otherwise be sorted at the top
const leadingNonAlphaRegex = /^[^\w]/;
// sort the platforms alphabetically
const sortedPlatforms = extractPlatforms(rootNode).sort((a, b) =>
(a.title ?? a.name)
.replace(leadingNonAlphaRegex, '')
.localeCompare((b.title ?? b.name).replace(leadingNonAlphaRegex, ''))
);

export function Home() {
return (
<div className="tw-app">
<Header pathname="/" searchPlatforms={[]} />
Expand Down Expand Up @@ -57,7 +46,7 @@ export async function Home() {
</div>
</div>

<PlatformFilter platforms={sortedPlatforms} />
<PlatformFilter />
<h2 className="text-2xl mt-16 mb-6 font-medium">Get to know us</h2>
<div className="flex flex-wrap gap-6">
<Card
Expand Down
196 changes: 196 additions & 0 deletions src/components/platformFilter/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
'use client';
import {useMemo, useState} from 'react';
import * as Collapsible from '@radix-ui/react-collapsible';
import {TriangleRightIcon} from '@radix-ui/react-icons';
import classNames from 'classnames';
import {matchSorter} from 'match-sorter';
import Link from 'next/link';

import {type Platform} from 'sentry-docs/types';
import {splitToChunks, uniqByReference} from 'sentry-docs/utils';

import styles from './style.module.scss';

import {PlatformIcon} from '../platformIcon';

export function PlatformFilterClient({platforms}: {platforms: Platform[]}) {
const platformsAndGuides = platforms
.map(p => [
p,
p.guides.map(g => ({...g, platform: p})),
p.integrations.map(integ => ({...integ, platform: p})),
])
.flat(2);
const [filter, setFilter] = useState('');

const matches = useMemo(() => {
if (!filter) {
return platformsAndGuides;
}
// any of these fields can be used to match the search value
const keys = ['title', 'aliases', 'name', 'sdk', 'keywords'];
const matches_ = matchSorter(platformsAndGuides, filter, {keys});
return matches_;
}, [filter, platformsAndGuides]);

const platformColumns: Platform[][] = splitToChunks(
3,
uniqByReference(matches.map(x => (x.type === 'platform' ? x : x.platform))).map(p => {
return {
...p,
guides: matches
.filter(m => m.type === 'guide' && m.platform.key === p.key)
.map(m => p.guides.find(g => g.key === m.key)!)
.filter(Boolean),
integrations: p.integrations.filter(i => matches.some(m => m.key === i.key)),
};
})
);

return (
<div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 py-8 md:items-end">
<div className="lg:col-span-2 space-y-2">
<h2 className="text-2xl font-medium">Choose your SDK</h2>
<p className="m-0">If you use it, we probably support it.</p>
</div>
<div className="w-full flex justify-end">
<input
placeholder="Search SDKs"
className={`${styles.input}`}
value={filter}
onChange={e => setFilter(e.target.value)}
/>
</div>
</div>
{matches.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{platformColumns.map((column, i) => (
<div key={i} className={`flex flex-col gap-4 ${styles.platform}`}>
{column.map(platform =>
platform.guides.length === 0 && platform.integrations.length === 0 ? (
<Link
href={platform.url}
key={platform.key}
style={{
textDecoration: 'none',
color: 'var(--foreground) !important',
}}
>
<div className={styles.StandalonePlatform}>
<PlatformIcon
size={20}
platform={platform.icon ?? platform.key}
format="lg"
className={styles.PlatformIcon}
/>
{platform.title}
</div>
</Link>
) : (
<PlatformWithGuides
key={platform.key}
platform={platform}
// force expand if the filter is long enough to have few results
forceExpand={filter.length >= 2}
/>
)
)}
</div>
))}
</div>
)}
{!matches.length && (
<div className="col-span-3 text-center text-gray-600">No SDKs found</div>
)}
</div>
);
}

function PlatformWithGuides({
platform,
forceExpand,
}: {
forceExpand: boolean;
platform: Platform;
}) {
const [expanded, setExpanded] = useState(false);
return (
<Collapsible.Root
className={styles.CollapsibleRoot}
open={forceExpand || expanded}
onOpenChange={setExpanded}
>
<Collapsible.Trigger asChild className={classNames(styles.CollapsibleTrigger)}>
<div>
<div className={styles.PlatformTitle} key={platform.key}>
<PlatformIcon
size={20}
platform={platform.icon ?? platform.key}
format="lg"
className={styles.PlatformIcon}
/>
{platform.title}
</div>
<button className={styles.ChevronButton}>
<TriangleRightIcon
className={styles.CollapsibleChevron}
aria-hidden
width="20"
height="20"
/>
</button>
</div>
</Collapsible.Trigger>
<Collapsible.Content
className={classNames(styles.CollapsibleContent)}
// scrollable if there are more than 8 (arbitrary limit) guides
data-scrollable={platform.guides.length >= 8 || platform.integrations.length >= 8}
>
{[platform, ...platform.guides].map((guide, i) => (
<Link
href={guide.url}
style={{textDecoration: 'none', color: 'var(--foreground) !important'}}
key={guide.key}
>
<div
className={styles.Guide}
style={{
animationDelay: `${i * 5}ms`,
}}
>
<PlatformIcon
size={20}
platform={guide.icon ?? guide.key}
format="lg"
className={styles.PlatformIcon}
/>
{guide.title}
</div>
</Link>
))}
{platform.guides.length === 0 &&
platform.integrations.map((integ, i) => {
return (
<Link href={integ.url} key={integ.key}>
<div
className={styles.Guide}
style={{
animationDelay: `${i * 5}ms`,
}}
>
<PlatformIcon
size={20}
format="lg"
platform={integ.icon}
className={styles.Platform}
/>
{integ.name}
</div>
</Link>
);
})}
</Collapsible.Content>
</Collapsible.Root>
);
}
Loading
Loading