Skip to content

Commit 5342299

Browse files
🎈Add sticky header
1 parent 405be69 commit 5342299

File tree

4 files changed

+211
-23
lines changed

4 files changed

+211
-23
lines changed

docs/accounts-billing/new-pricing-comparison.md

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
slug: plan-comparison
3-
title: "Pricing Comparison"
3+
title: "Plan Comparison"
44
description: "Compare FlutterFlow plans and features to find the right plan for your needs"
5+
hide_table_of_contents: true
56
---
67

78
<Head>
@@ -17,10 +18,10 @@ Choose the plan that fits your development needs and team size.
1718
← Scroll horizontally to see all plans →
1819
</div>
1920

20-
<table className="simplified-pricing-table">
21-
<thead>
21+
<table className="simplified-pricing-table" id="pricing-table">
22+
<thead id="table-header">
2223
<tr>
23-
<th>Features</th>
24+
<th>Plan</th>
2425
<th>Free<br/><span style={{fontSize: '1.2em', fontWeight: 'bold'}}>$0</span><br/><span style={{fontSize: '0.9em', opacity: '0.8'}}>per month</span></th>
2526
<th>Individual<br/><span style={{fontSize: '1.2em', fontWeight: 'bold'}}>$39</span><br/><span style={{fontSize: '0.9em', opacity: '0.8'}}>per month</span></th>
2627
<th>Growth<br/><span style={{fontSize: '1.2em', fontWeight: 'bold'}}>$135</span><br/><span style={{fontSize: '0.9em', opacity: '0.8'}}>per month</span></th>
@@ -397,10 +398,18 @@ Choose the plan that fits your development needs and team size.
397398
<td>✅</td>
398399
</tr>
399400
<tr>
400-
<td>Git-Style Visual Branching<br/><span className="feature-description">Create and manage project branches visually</span></td>
401-
<td>❌</td>
402-
<td>❌</td>
403-
<td>❌</td>
401+
<td>Manual Commits<br/><span className="feature-description">Make explicit named commits to the current branch for version control</span></td>
402+
<td>✅</td>
403+
<td>✅</td>
404+
<td>✅</td>
405+
<td>✅</td>
406+
<td>✅</td>
407+
</tr>
408+
<tr>
409+
<td>Number of Branches<br/><span className="feature-description">Create and manage multiple project branches (all plans include main branch)</span></td>
410+
<td>1 (main only)</td>
411+
<td>1 (main only)</td>
412+
<td>1 (main only)</td>
404413
<td>Up to 5 open branches</td>
405414
<td>Unlimited</td>
406415
</tr>
@@ -432,7 +441,7 @@ Choose the plan that fits your development needs and team size.
432441
<td>Centralized Billing<br/><span className="feature-description">Manage team billing from one account</span></td>
433442
<td>❌</td>
434443
<td>❌</td>
435-
<td></td>
444+
<td></td>
436445
<td>✅</td>
437446
<td>✅</td>
438447
</tr>
@@ -520,6 +529,14 @@ Choose the plan that fits your development needs and team size.
520529
<tr className="section-header">
521530
<td colSpan="6"><strong>Support</strong></td>
522531
</tr>
532+
<tr>
533+
<td>Account and Billing Support<br/><span className="feature-description">Help with account management and billing questions</span></td>
534+
<td>✅</td>
535+
<td>✅</td>
536+
<td>✅</td>
537+
<td>✅</td>
538+
<td>✅</td>
539+
</tr>
523540
<tr>
524541
<td>Community Support<br/><span className="feature-description">Access to FlutterFlow community forums</span></td>
525542
<td>✅</td>

docusaurus.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ const config: Config = {
1111
onBrokenMarkdownLinks: 'throw',
1212
onBrokenAnchors: 'throw',
1313
favicon: 'logos/favicon.png',
14+
clientModules: [
15+
require.resolve('./src/js/table-helpers.js'),
16+
],
1417
i18n: {
1518
defaultLocale: 'en',
1619
locales: ['en'],

src/js/table-helpers.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";
2+
3+
let floatingHeader = null;
4+
let cleanup = null;
5+
6+
function createFloatingHeader(originalHeader) {
7+
if (floatingHeader) return floatingHeader;
8+
9+
// Get the original table for width measurements
10+
const originalTable = originalHeader.closest("table");
11+
12+
// Clone the original header
13+
const headerClone = originalHeader.cloneNode(true);
14+
15+
// Create floating container
16+
floatingHeader = document.createElement("div");
17+
18+
// Check if we're on a wide screen where sidebar is visible
19+
const isWideScreen = window.innerWidth >= 997;
20+
21+
// Get the actual sidebar width from CSS variables
22+
let sidebarWidth = "0px";
23+
if (isWideScreen) {
24+
const computedStyle = getComputedStyle(document.documentElement);
25+
const docSidebarWidth = computedStyle.getPropertyValue(
26+
"--doc-sidebar-width"
27+
);
28+
sidebarWidth = docSidebarWidth || "300px"; // fallback to 300px
29+
}
30+
31+
floatingHeader.style.cssText = `
32+
position: fixed;
33+
top: 60px;
34+
left: ${sidebarWidth};
35+
right: 0;
36+
z-index: 1000;
37+
background: var(--ifm-background-color);
38+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
39+
display: none;
40+
overflow-x: auto;
41+
padding: 0 20px;
42+
`;
43+
44+
// Create table wrapper for the floating header
45+
const floatingTable = document.createElement("table");
46+
floatingTable.className = "simplified-pricing-table";
47+
floatingTable.style.cssText = `
48+
margin: 0 auto;
49+
width: 100%;
50+
max-width: 1200px;
51+
table-layout: fixed;
52+
border-collapse: collapse;
53+
`;
54+
55+
floatingTable.appendChild(headerClone);
56+
floatingHeader.appendChild(floatingTable);
57+
document.body.appendChild(floatingHeader);
58+
59+
// Copy column widths from original table to floating table
60+
const originalCells = originalHeader.querySelectorAll("th");
61+
const floatingCells = headerClone.querySelectorAll("th");
62+
63+
// Get the computed width of the original table
64+
const originalTableRect = originalTable.getBoundingClientRect();
65+
floatingTable.style.width = originalTableRect.width + "px";
66+
67+
originalCells.forEach((originalCell, index) => {
68+
if (floatingCells[index]) {
69+
const cellRect = originalCell.getBoundingClientRect();
70+
floatingCells[index].style.width = cellRect.width + "px";
71+
floatingCells[index].style.minWidth = cellRect.width + "px";
72+
floatingCells[index].style.maxWidth = cellRect.width + "px";
73+
}
74+
});
75+
76+
return floatingHeader;
77+
}
78+
79+
function handleScroll(originalHeader) {
80+
if (!originalHeader) return;
81+
82+
const headerRect = originalHeader.getBoundingClientRect();
83+
const isHeaderVisible = headerRect.bottom > 60; // Account for navbar
84+
85+
if (!isHeaderVisible) {
86+
// Original header is out of view, show floating header
87+
const floating = createFloatingHeader(originalHeader);
88+
floating.style.display = "block";
89+
} else {
90+
// Original header is visible, hide floating header
91+
if (floatingHeader) {
92+
floatingHeader.style.display = "none";
93+
}
94+
}
95+
}
96+
97+
function initFloatingHeader() {
98+
// Clean up any existing setup
99+
if (cleanup) cleanup();
100+
101+
const table = document.getElementById("pricing-table");
102+
const originalHeader = document.getElementById("table-header");
103+
104+
if (!table || !originalHeader) return;
105+
106+
const scrollHandler = () => handleScroll(originalHeader);
107+
108+
// Use passive event listeners for better performance
109+
window.addEventListener("scroll", scrollHandler, { passive: true });
110+
111+
// Handle window resize to update column widths
112+
const resizeHandler = () => {
113+
if (floatingHeader) {
114+
// Remove and recreate floating header with new measurements
115+
if (floatingHeader.parentNode) {
116+
floatingHeader.parentNode.removeChild(floatingHeader);
117+
}
118+
floatingHeader = null;
119+
}
120+
};
121+
window.addEventListener("resize", resizeHandler, { passive: true });
122+
123+
// Cleanup function
124+
cleanup = () => {
125+
window.removeEventListener("scroll", scrollHandler);
126+
window.removeEventListener("resize", resizeHandler);
127+
if (floatingHeader && floatingHeader.parentNode) {
128+
floatingHeader.parentNode.removeChild(floatingHeader);
129+
floatingHeader = null;
130+
}
131+
};
132+
}
133+
134+
if (ExecutionEnvironment.canUseDOM) {
135+
// Initialize on route changes
136+
if (typeof window !== "undefined") {
137+
// Safari might need a longer delay
138+
const isSafari =
139+
navigator.userAgent.includes("Safari") &&
140+
!navigator.userAgent.includes("Chrome");
141+
const delay = isSafari ? 300 : 100;
142+
143+
// Initial setup
144+
setTimeout(initFloatingHeader, delay);
145+
146+
// Setup for route changes
147+
const originalPushState = history.pushState;
148+
const originalReplaceState = history.replaceState;
149+
150+
const onRouteChange = () => {
151+
setTimeout(initFloatingHeader, delay);
152+
};
153+
154+
history.pushState = function (...args) {
155+
originalPushState.apply(this, args);
156+
onRouteChange();
157+
};
158+
159+
history.replaceState = function (...args) {
160+
originalReplaceState.apply(this, args);
161+
onRouteChange();
162+
};
163+
164+
window.addEventListener("popstate", onRouteChange);
165+
}
166+
}

static/css/table_style.css

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
* Pricing table styles for plan-pricing.md
33
*/
44

5-
.pricing-table-container {
5+
/* Pricing table container */
6+
.pricing-table-container {
67
max-width: 1200px;
78
margin: 0 auto;
8-
overflow: hidden;
9+
overflow: auto;
910
}
1011

1112
.mobile-scroll-hint {
@@ -46,21 +47,10 @@
4647
vertical-align: top;
4748
}
4849

49-
/* Light mode table borders */
50-
[data-theme='light'] .simplified-pricing-table th,
51-
[data-theme='light'] .simplified-pricing-table td {
52-
border-bottom: 1px solid #ebeaf0;
53-
}
54-
55-
/* Dark mode table borders */
56-
[data-theme='dark'] .simplified-pricing-table th,
57-
[data-theme='dark'] .simplified-pricing-table td {
58-
border-bottom: 1px solid #404040;
59-
}
60-
6150
.simplified-pricing-table th {
6251
background-color: #f5f5f5;
6352
font-weight: 600;
53+
border-bottom: 2px solid #ddd;
6454
}
6555

6656
/* Light mode header background */
@@ -75,6 +65,18 @@
7565
color: #ffffff;
7666
}
7767

68+
/* Light mode table borders */
69+
[data-theme='light'] .simplified-pricing-table th,
70+
[data-theme='light'] .simplified-pricing-table td {
71+
border-bottom: 1px solid #ebeaf0;
72+
}
73+
74+
/* Dark mode table borders */
75+
[data-theme='dark'] .simplified-pricing-table th,
76+
[data-theme='dark'] .simplified-pricing-table td {
77+
border-bottom: 1px solid #404040;
78+
}
79+
7880
.whats-changing-col {
7981
width: 50%;
8082
}

0 commit comments

Comments
 (0)