Skip to content

Commit e638e68

Browse files
authored
new pricing plans table (#6581)
1 parent 06e7012 commit e638e68

File tree

16 files changed

+688
-116
lines changed

16 files changed

+688
-116
lines changed

.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ module.exports = {
231231
'nextra-scrollbar',
232232
'no-scrollbar', // from Nextra
233233
'hive-slider',
234+
'subheader',
234235
],
235236
config: path.join(__dirname, './packages/web/docs/tailwind.config.ts'),
236237
},

packages/web/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"devDependencies": {
3030
"@tailwindcss/typography": "0.5.16",
31-
"@theguild/tailwind-config": "0.6.2",
31+
"@theguild/tailwind-config": "0.6.3",
3232
"@types/react": "18.3.18",
3333
"@types/rss": "^0.0.32",
3434
"next-sitemap": "4.2.3",

packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { use } from 'react';
44
import { cn, ComparisonTable as Table } from '@theguild/components';
5-
import { functionalTones } from './functional-tones';
65
import { CheckmarkIcon, XIcon } from './icons';
76

87
interface BenchmarkDatum {
@@ -47,48 +46,35 @@ export function BenchmarkTableBody() {
4746
>
4847
<div className="flex items-center gap-2.5 whitespace-nowrap">
4948
<div
50-
className="size-3 rounded-full"
51-
style={{
52-
background:
53-
compatibility > 99
54-
? functionalTones.positiveBright
55-
: compatibility > 90
56-
? functionalTones.warning
57-
: functionalTones.criticalBright,
58-
}}
49+
className={cn(
50+
'size-3 rounded-full',
51+
compatibility > 99
52+
? 'bg-positive-bright'
53+
: compatibility > 90
54+
? 'bg-warning-bright'
55+
: 'bg-critical-bright',
56+
)}
5957
/>
6058
{row.name}
6159
</div>
6260
</Table.Cell>
6361
<Table.Cell className="text-sm text-green-800">{compatibility.toFixed(2)}%</Table.Cell>
6462
<Table.Cell>
65-
<span
66-
className="inline-flex items-center gap-0.5 text-sm"
67-
style={{ color: functionalTones.positiveDark }}
68-
>
63+
<span className="text-positive-dark inline-flex items-center gap-0.5 text-sm">
6964
<CheckmarkIcon className="size-4" /> {row.cases.passed}
7065
</span>
7166
{row.cases.failed > 0 && (
72-
<span
73-
className="ml-2 inline-flex items-center text-sm"
74-
style={{ color: functionalTones.criticalDark }}
75-
>
67+
<span className="text-critical-dark ml-2 inline-flex items-center text-sm">
7668
<XIcon className="size-4" /> {row.cases.failed}
7769
</span>
7870
)}
7971
</Table.Cell>
8072
<Table.Cell>
81-
<span
82-
className="inline-flex items-center gap-0.5 text-sm"
83-
style={{ color: functionalTones.positiveDark }}
84-
>
73+
<span className="text-positive-dark inline-flex items-center gap-0.5 text-sm">
8574
<CheckmarkIcon className="size-4" /> {row.suites.passed}
8675
</span>
8776
{row.suites.failed > 0 && (
88-
<span
89-
className="ml-2 inline-flex items-center text-sm"
90-
style={{ color: functionalTones.criticalDark }}
91-
>
77+
<span className="text-critical-dark ml-2 inline-flex items-center text-sm">
9278
<XIcon className="size-4" /> {row.suites.failed}
9379
</span>
9480
)}

packages/web/docs/src/app/gateway/federation-compatible-benchmarks/functional-tones.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Suspense } from 'react';
22
import { CallToAction, cn, Heading, ComparisonTable as Table } from '@theguild/components';
33
import { BenchmarkTableBody } from './benchmark-table-body';
4-
import { functionalTones } from './functional-tones';
54
import { CheckmarkIcon, XIcon } from './icons';
65

76
export interface FederationCompatibleBenchmarksSectionProps
@@ -90,26 +89,22 @@ function BenchmarkLegend() {
9089
<div className="mt-6 flex flex-wrap gap-2 whitespace-nowrap text-xs text-green-800 sm:gap-4">
9190
<div className="flex gap-2 max-sm:-mx-1 max-sm:w-full sm:contents">
9291
<div className="flex items-center gap-1">
93-
<CheckmarkIcon className="size-4" style={{ color: functionalTones.positiveDark }} />{' '}
94-
Passed tests
92+
<CheckmarkIcon className="text-positive-dark size-4" /> Passed tests
9593
</div>
9694
<div className="flex items-center gap-1">
97-
<XIcon className="size-4" style={{ color: functionalTones.criticalDark }} /> Failed tests
95+
<XIcon className="text-critical-dark size-4" /> Failed tests
9896
</div>
9997
</div>
10098
<div className="flex items-center gap-2">
101-
<div
102-
className="size-2 rounded-full"
103-
style={{ background: functionalTones.positiveBright }}
104-
/>
99+
<div className="bg-positive-bright size-2 rounded-full" />
105100
Perfect compatibility
106101
</div>
107102
<div className="flex items-center gap-2">
108-
<div className="size-2 rounded-full" style={{ background: functionalTones.warning }} />
103+
<div className="bg-warning-bright size-2 rounded-full" />
109104
75% and higher
110105
</div>
111106
<div className="flex items-center gap-2">
112-
<div className="size-2 rounded-full" style={{ background: functionalTones.criticalDark }} />
107+
<div className="bg-critical-dark size-2 rounded-full" />
113108
Less than 75%
114109
</div>
115110
</div>

packages/web/docs/src/app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { DynamicMetaTags } from './dynamic-meta-tags';
1717
import graphQLConfLocalImage from '../components/graphql-conf-image.webp';
1818
import '@theguild/components/style.css';
1919
import '../selection-styles.css';
20+
import '../easing-functions.css';
2021
import '../mermaid.css';
2122
import { NarrowPages } from './narrow-pages';
2223

packages/web/docs/src/app/pricing/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { FrequentlyAskedQuestions } from '../../components/frequently-asked-ques
1010
import { LandingPageContainer } from '../../components/landing-page-container';
1111
import { PlanComparison } from '../../components/plan-comparison';
1212
import { Pricing } from '../../components/pricing';
13+
import { PlansTable } from '../../components/pricing/plans-table';
1314

1415
export const metadata = {
1516
title: 'Hive Platform Pricing',
@@ -26,6 +27,8 @@ export default function PricingPage() {
2627

2728
<PlanComparison className="mx-4 md:mx-6" />
2829

30+
<PlansTable />
31+
2932
<CompanyTestimonialsSection className="mx-4 mt-6 md:mx-6" />
3033

3134
<FrequentlyAskedQuestions className="mx-4 md:mx-6" />

packages/web/docs/src/components/company-testimonials/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export function CompanyTestimonialsSection({ className }: { className?: string }
167167
value={company}
168168
tabIndex={-1}
169169
className={cn(
170-
'relative flex w-full shrink-0 snap-center flex-col',
170+
'relative flex w-full shrink-0 snap-center flex-col outline-none',
171171
'gap-6 md:flex-row lg:gap-12',
172172
'lg:data-[state="inactive"]:hidden',
173173
caseStudyHref
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// these are different than CheckIcon and CloseIcon we have in the design system
2+
3+
export function CheckmarkIcon(props: React.SVGProps<SVGSVGElement>) {
4+
return (
5+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" {...props}>
6+
<path d="M6.66668 10.1134L12.7947 3.98608L13.7373 4.92875L6.66668 11.9994L2.42401 7.75675L3.36668 6.81408L6.66668 10.1134Z" />
7+
</svg>
8+
);
9+
}
10+
11+
export function XIcon(props: React.SVGProps<SVGSVGElement>) {
12+
return (
13+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" {...props}>
14+
<path d="M7.99999 7.05806L11.3 3.75806L12.2427 4.70072L8.94266 8.00072L12.2427 11.3007L11.2993 12.2434L7.99932 8.94339L4.69999 12.2434L3.75732 11.3001L7.05732 8.00006L3.75732 4.70006L4.69999 3.75872L7.99999 7.05806Z" />
15+
</svg>
16+
);
17+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
'use client';
2+
3+
import { ReactNode, useEffect, useRef } from 'react';
4+
5+
const BOTTOM_THRESHOLD_ADJUSTMENT = 10;
6+
7+
interface NestedStickyProps {
8+
children: ReactNode;
9+
offsetTop: number;
10+
offsetBottom: number;
11+
zIndex?: number;
12+
}
13+
14+
/**
15+
* `position: sticky` doesn't work in nested divs with overflow-x-hidden,
16+
* and restructuring the app to put pricing table header on top level would
17+
* require tricky state management, so we have this for the cases where we
18+
* need position: sticky, but can't use it directly.
19+
*/
20+
export function NestedSticky({
21+
children,
22+
offsetTop,
23+
offsetBottom,
24+
zIndex = 10,
25+
}: NestedStickyProps) {
26+
const containerRef = useRef<HTMLDivElement>(null);
27+
28+
useEffect(() => {
29+
const container = containerRef.current;
30+
if (!container) return;
31+
32+
const placeholder = container.firstElementChild as HTMLElement;
33+
const sticky = container.lastElementChild as HTMLElement;
34+
const parent = container.parentElement;
35+
36+
if (!placeholder || !sticky || !parent) return;
37+
38+
let width = 0;
39+
let height = 0;
40+
let rafId: number | null = null;
41+
42+
// relative at the top
43+
// fixed when we scroll
44+
// absolute when we're near the bottom
45+
type State = 'fixed' | 'absolute' | 'relative';
46+
let state: State = 'relative';
47+
let prevState: State = 'relative';
48+
49+
const measureDimensions = () => {
50+
const rect = sticky.getBoundingClientRect();
51+
width = rect.width;
52+
height = rect.height;
53+
54+
sticky.style.zIndex = String(zIndex);
55+
};
56+
57+
const updateStyles = () => {
58+
placeholder.style.height = state !== 'relative' ? `${height}px` : '0';
59+
60+
if (state === 'fixed') {
61+
sticky.style.position = 'fixed';
62+
sticky.style.top = `${offsetTop}px`;
63+
sticky.style.width = `${width}px`;
64+
sticky.setAttribute('data-sticky', 'fixed');
65+
} else if (state === 'absolute') {
66+
const containerRect = container.getBoundingClientRect();
67+
const stickyRect = sticky.getBoundingClientRect();
68+
69+
const relativeTop = stickyRect.top - containerRect.top;
70+
71+
sticky.style.position = 'absolute';
72+
sticky.style.top = `${relativeTop}px`;
73+
sticky.style.width = `${width}px`;
74+
sticky.setAttribute('data-sticky', 'absolute');
75+
} else {
76+
sticky.style.position = 'relative';
77+
sticky.style.top = '';
78+
sticky.style.width = '';
79+
sticky.removeAttribute('data-sticky');
80+
}
81+
};
82+
83+
const handleScroll = () => {
84+
if (rafId) {
85+
cancelAnimationFrame(rafId);
86+
}
87+
88+
rafId = requestAnimationFrame(() => {
89+
const containerRect = container.getBoundingClientRect();
90+
const parentRect = parent.getBoundingClientRect();
91+
92+
const shouldBeFixed = containerRect.top < offsetTop;
93+
94+
const nearBottom =
95+
parentRect.bottom < offsetTop + height + offsetBottom + BOTTOM_THRESHOLD_ADJUSTMENT;
96+
97+
state = shouldBeFixed && nearBottom ? 'absolute' : shouldBeFixed ? 'fixed' : 'relative';
98+
99+
if (state !== prevState) {
100+
prevState = state;
101+
updateStyles();
102+
}
103+
});
104+
};
105+
106+
const handleResize = () => {
107+
const placeholderRect = placeholder.getBoundingClientRect();
108+
109+
width = placeholderRect.width;
110+
111+
updateStyles();
112+
handleScroll();
113+
};
114+
115+
measureDimensions();
116+
handleScroll();
117+
118+
window.addEventListener('resize', handleResize, { passive: true });
119+
window.addEventListener('scroll', handleScroll, { passive: true });
120+
121+
return () => {
122+
if (rafId) {
123+
cancelAnimationFrame(rafId);
124+
}
125+
126+
window.removeEventListener('resize', handleResize);
127+
window.removeEventListener('scroll', handleScroll);
128+
};
129+
}, [offsetTop, offsetBottom, zIndex]);
130+
131+
return (
132+
<div ref={containerRef} className="relative">
133+
<div style={{ width: '100%', height: 0 }} />
134+
<div>{children}</div>
135+
</div>
136+
);
137+
}

0 commit comments

Comments
 (0)