Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion apps/marketing/scripts/generate-search-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const docsContentPath = path.join(__dirname, "../src/components/pages/docs-pages
const outputPath = path.join(__dirname, "../src/constants/docsSearchIndex.json");

/** Temporarily hidden from nav/search. */
const HIDDEN_DOC_IDS = new Set(["column-editing"]);
const HIDDEN_DOC_IDS = new Set(["column-editing", "tdgp"]);

/**
* Slug → Content filename when PascalCase conversion does not match the file on disk.
Expand Down
39 changes: 39 additions & 0 deletions apps/marketing/src/app/docs/tdgp/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Metadata } from "next";
import { SEO_STRINGS } from "@/constants/strings/seo";
import TdgpContent from "@/components/pages/docs-pages/TdgpContent";
import DocsDemoCode from "@/components/DocsDemoCode";

// Unreleased: reachable by URL, but not in nav, search, or the sitemap.
export const metadata: Metadata = {
title: SEO_STRINGS.tdgp.title,
description: SEO_STRINGS.tdgp.description,
keywords: SEO_STRINGS.tdgp.keywords,
robots: { index: false, follow: false },
openGraph: {
title: SEO_STRINGS.tdgp.title,
description: SEO_STRINGS.tdgp.description,
type: "article",
images: [SEO_STRINGS.site.ogImage],
siteName: SEO_STRINGS.site.name,
},
twitter: {
card: "summary_large_image",
title: SEO_STRINGS.tdgp.title,
description: SEO_STRINGS.tdgp.description,
creator: SEO_STRINGS.site.creator,
images: SEO_STRINGS.site.ogImage.url,
},
alternates: {
canonical: "/docs/tdgp",
},
};

const TdgpPage = () => {
return (
<DocsDemoCode slug="tdgp">
<TdgpContent />
</DocsDemoCode>
);
};

export default TdgpPage;
1 change: 1 addition & 0 deletions apps/marketing/src/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ const NON_INDEXABLE_SEGMENTS = new Set([
"/mobile-unsupported",
// Temporarily hidden from sitemap; page route still exists but is unlinked.
"/docs/column-editing",
"/docs/tdgp",
// Internal reproduction page; reachable by direct URL only.
"/sandbox/context-isolation",
// Internal marketing ops checklist; noindex.
Expand Down
226 changes: 226 additions & 0 deletions apps/marketing/src/components/pages/docs-pages/TdgpContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
"use client";

import type { ReactNode } from "react";
import Link from "next/link";
import { motion } from "framer-motion";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlug } from "@fortawesome/free-solid-svg-icons";
import DocNavigationButtons from "@/components/DocNavigationButtons";
import PageWrapper from "@/components/PageWrapper";
import CodeBlock from "@/components/CodeBlock";
import PropTable, { type PropInfo } from "@/components/PropTable";
import { tdgpSnippets, type CodeByFramework } from "@/constants/docsSnippets";

type TdgpPattern = {
title: string;
body: ReactNode;
codeByFramework: CodeByFramework;
};

const TDGP_PATTERNS: TdgpPattern[] = [
{
title: "Connect to a TDGP server",
body: (
<>
Install{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">@thedatagrid/client</code>{" "}
next to Simple Table. Call{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">useTdgpTable</code> in
React, Vue, Solid, or Angular,{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">createTdgpTable</code> in
Svelte, or{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">mountTdgpTable</code> in
vanilla JavaScript. Pass{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">tableProps</code> to the
table (Angular uses the{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">tableProps</code> input).
Page changes, sorts, and column filters become server requests. Pair with{" "}
<Link
href="/docs/loading-state"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
isLoading
</Link>{" "}
(already set on{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">tableProps</code>).
</>
),
codeByFramework: tdgpSnippets(),
},
];

const TDGP_PROPS: PropInfo[] = [
{
key: "client",
name: "client",
required: true,
description:
"A TDGP client with a query(dataset, request) method. createTdgpClient({ url }) from @thedatagrid/client matches this.",
type: "TdgpQueryClient",
example: `client={createTdgpClient({ url: "https://data.thedatagrid.com" })}`,
},
{
key: "dataset",
name: "dataset",
required: true,
description:
"Dataset name on the TDGP server. This is the route segment (POST /{dataset}/query), not a URL. The public catalog lists developers-10k.",
type: "string",
example: `dataset="developers-10k"`,
},
{
key: "columns",
name: "columns",
required: true,
description: "Column definitions for the table. Keep this array stable across renders.",
type: "ColumnDef[]",
example: `columns={columns}`,
},
{
key: "pageSize",
name: "pageSize",
required: false,
description:
"Rows per page. Sent to the server as limit (start is (page - 1) × pageSize). Defaults to 50.",
type: "number",
example: `pageSize={50}`,
},
{
key: "primaryKey",
name: "primaryKey",
required: false,
description:
"Field used as the row id for leaf rows. Read this from the dataset catalog (GET /datasets). The public developers-10k dataset uses id. If you omit it, the helper falls back to id.",
type: "string",
example: `primaryKey="id"`,
},
{
key: "groupBy",
name: "groupBy",
required: false,
description:
"Group on the server by these catalog field names. Expanding a group loads the next level, or the leaf rows when you have expanded every group field.",
type: "string[]",
example: `groupBy={["country", "stack"]}`,
},
{
key: "aggregations",
name: "aggregations",
required: false,
description:
"Server aggregations for grouped rows (sum, avg, min, max, count). Each id is copied onto the group row, so use a name that will not collide with a field (the protocol examples use avgSalary).",
type: "TdgpAggregation[]",
example: `aggregations={[{ id: "avgSalary", field: "salary", fn: "avg" }]}`,
},
];

const TdgpContent = () => {
return (
<PageWrapper>
<motion.div
className="flex items-center gap-3 mb-6"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<div className="p-2 bg-blue-100 rounded-lg">
<FontAwesomeIcon icon={faPlug} className="text-blue-600 text-2xl" />
</div>
<h1 className="text-3xl font-bold text-gray-800 dark:text-white">Server Data (TDGP)</h1>
</motion.div>

<motion.p
className="text-gray-700 dark:text-gray-300 mb-8 text-lg"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
THE DataGrid Protocol (TDGP) is a shared JSON contract for asking a server for a page of
rows — filtered, sorted, and optionally grouped. Simple Table maps its existing{" "}
<Link
href="/docs/pagination"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
server-side pagination
</Link>
, sort, and filter hooks onto that contract. The protocol is documented at{" "}
<a
href="https://thedatagrid.com/protocol"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
thedatagrid.com/protocol
</a>
. The public demo server is{" "}
<a
href="https://data.thedatagrid.com"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
data.thedatagrid.com
</a>
, with a live API reference at{" "}
<a
href="https://data.thedatagrid.com/docs"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
/docs
</a>
.
</motion.p>

<motion.div
className="space-y-8 mb-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.25 }}
>
{TDGP_PATTERNS.map((pattern) => (
<section key={pattern.title}>
<h2 className="text-xl font-semibold text-gray-800 dark:text-white mb-2">
{pattern.title}
</h2>
<p className="text-sm text-gray-700 dark:text-gray-300 mb-3">{pattern.body}</p>
<CodeBlock codeByFramework={pattern.codeByFramework} showLineNumbers={false} />
</section>
))}
</motion.div>

<motion.div
className="mb-8 p-4 bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700 rounded-lg"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.28 }}
>
<h3 className="font-semibold text-gray-800 dark:text-white mb-1 text-sm">Grouping</h3>
<p className="text-gray-700 dark:text-gray-300 text-sm">
Pass{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">groupBy</code> (field
names from the catalog) to load group rows first. Use the{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">columns</code> from the
snapshot so the first group column can expand. Expanding a group fetches the next level
from the server.{" "}
<code className="bg-gray-200 dark:bg-gray-700 px-1 py-0.5 rounded">totalCount</code> at a
group level is the number of groups, not leaf rows. Pivot stays client-side — use{" "}
<Link href="/docs/pivot" className="text-blue-600 dark:text-blue-400 hover:underline">
Pivot Tables
</Link>{" "}
on rows you already have.
</p>
</motion.div>

<motion.h2
className="text-2xl font-bold text-gray-800 dark:text-white mb-4 flex items-center gap-2 pb-2 border-b border-gray-200 dark:border-gray-700"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.4 }}
>
Options
</motion.h2>

<PropTable props={TDGP_PROPS} title="useTdgpTable / createTdgpTable / mountTdgpTable" />

<DocNavigationButtons />
</PageWrapper>
);
};

export default TdgpContent;
16 changes: 16 additions & 0 deletions apps/marketing/src/constants/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ export interface ChangelogEntry {
}[];
}

export const v4_1_9: ChangelogEntry = {
version: "4.1.9",
date: "2026-08-22",
title: "Server data with TDGP",
description:
"Connect the table to a THE DataGrid Protocol server so pages, sorts, filters, and groups load from the server.",
changes: [
{
type: "feature",
description:
"Connect the table to a THE DataGrid Protocol (TDGP) server. Page changes, sorts, and filters load from the server — and you can group on the server too.",
},
],
};

export const v4_1_8: ChangelogEntry = {
version: "4.1.8",
date: "2026-08-22",
Expand Down Expand Up @@ -2692,6 +2707,7 @@ export const v1_4_4: ChangelogEntry = {

// Array of all changelog entries (newest first)
export const CHANGELOG_ENTRIES: ChangelogEntry[] = [
v4_1_9,
v4_1_8,
v4_1_7,
v4_1_6,
Expand Down
1 change: 1 addition & 0 deletions apps/marketing/src/constants/docsSeo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const DOC_SLUG_TO_SEO_KEY: Record<string, SeoStringKey> = {
"row-height": "rowHeight",
"row-selection": "rowSelection",
"table-height": "tableHeight",
tdgp: "tdgp",
themes: "themes",
tooltips: "tooltips",
"value-formatter": "valueFormatter",
Expand Down
Loading
Loading