Skip to content
Merged
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
252 changes: 232 additions & 20 deletions apps/marketing/public/txt-demos/angular/pivot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,45 @@ import { pivotDemoConfig } from "./pivot.demo-data";
import type { PivotFact } from "./pivot.demo-data";
import "@simple-table/angular/styles.css";

const INITIAL_PIVOT: PivotConfig = {
rows: ["region", "product"],
columns: ["quarter"],
values: [{ accessor: "sales", aggregation: { type: "sum" } }],
};

@Component({
selector: "pivot-demo",
standalone: true,
imports: [SimpleTableComponent],
template: `
<simple-table
[getRowId]="getRowId"
[rows]="rows"
[columns]="headers"
[pivot]="pivot"
[onPivotChange]="onPivotChange"
[autoExpandColumns]="true"
[columnResizing]="true"
[enableColumnEditor]="true"
[enableColumnEditorInitOpen]="true"
[enablePivotPanel]="true"
[height]="height"
[selectableCells]="true"
[theme]="theme"
></simple-table>
<div>
<label
style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: #374151"
>
<input
type="checkbox"
[checked]="pivotEnabled"
aria-label="Pivot mode"
(change)="onPivotEnabledChange($any($event.target).checked)"
/>
Pivot mode
</label>
<simple-table
[getRowId]="getRowId"
[rows]="rows"
[columns]="headers"
[pivot]="pivotEnabled ? pivot : null"
[onPivotChange]="onPivotChange"
[autoExpandColumns]="true"
[columnResizing]="true"
[enableColumnEditor]="true"
[enableColumnEditorInitOpen]="true"
[enablePivotPanel]="pivotEnabled"
[height]="height"
[selectableCells]="true"
[theme]="theme"
></simple-table>
</div>
`,
})
export class PivotDemoComponent {
Expand All @@ -40,11 +59,15 @@ export class PivotDemoComponent {
readonly rows: PivotFact[] = pivotDemoConfig.rows;
readonly headers: AngularColumnDef<PivotFact>[] = pivotDemoConfig.headers;

pivot: PivotConfig | null = {
rows: ["region", "product"],
columns: ["quarter"],
values: [{ accessor: "sales", aggregation: { type: "sum" } }],
};
pivotEnabled = true;
pivot: PivotConfig | null = INITIAL_PIVOT;

onPivotEnabledChange(enabled: boolean): void {
this.pivotEnabled = enabled;
if (enabled && this.pivot === null) {
this.pivot = INITIAL_PIVOT;
}
}

onPivotChange = (next: PivotConfig | null) => {
this.pivot = next;
Expand All @@ -53,3 +76,192 @@ export class PivotDemoComponent {
getRowId = ({ row }: GetRowIdParams<PivotFact>) =>
row?.id == null ? undefined : String(row.id);
}


// pivot.demo-data.ts
import type { PivotConfig, AngularColumnDef, ValueFormatterProps } from "@simple-table/angular";

export interface PivotFact {
id: string;
region: string;
country: string;
category: string;
product: string;
channel: string;
year: number;
quarter: string;
sales: number;
units: number;
cost: number;
}

export const pivotHeaders: AngularColumnDef<PivotFact>[] = [
{ accessor: "region", label: "Region", width: 110, type: "string" },
{ accessor: "country", label: "Country", width: 100, type: "string" },
{ accessor: "category", label: "Category", width: 110, type: "string" },
{ accessor: "product", label: "Product", width: 120, type: "string" },
{ accessor: "channel", label: "Channel", width: 100, type: "string" },
{ accessor: "year", label: "Year", width: 80, type: "number" },
{ accessor: "quarter", label: "Quarter", width: 80, type: "string" },
{
accessor: "sales",
label: "Sales",
width: 100,
type: "number",
align: "right",
valueFormatter: ({ value }: ValueFormatterProps<PivotFact>) =>
typeof value === "number" ? `$${value.toLocaleString()}` : "",
},
{
accessor: "units",
label: "Units",
width: 80,
type: "number",
align: "right",
},
{
accessor: "cost",
label: "Cost",
width: 100,
type: "number",
align: "right",
valueFormatter: ({ value }: ValueFormatterProps<PivotFact>) =>
typeof value === "number" ? `$${value.toLocaleString()}` : "",
},
];

const COUNTRIES = {
West: ["USA", "Canada"],
East: ["USA", "UK"],
North: ["Canada", "Sweden"],
South: ["Brazil", "Australia"],
};
const PRODUCTS = {
Hardware: ["Widget", "Gadget", "Sensor"],
Software: ["License", "Subscription"],
};
const CHANNELS = ["Direct", "Partner", "Online"];
const YEARS = [2024, 2025];
const QUARTERS = ["Q1", "Q2", "Q3", "Q4"];

/** Sparse multi-dimension fact cube (~150–250 rows). */
export function generatePivotRows(): PivotFact[] {
const rows: PivotFact[] = [];
let id = 1;
for (const [region, countries] of Object.entries(COUNTRIES)) {
for (const country of countries) {
for (const [category, products] of Object.entries(PRODUCTS)) {
for (const product of products) {
for (const channel of CHANNELS) {
for (const year of YEARS) {
for (const quarter of QUARTERS) {
// Sparse facts — skip ~1/3 of combinations
if ((id + year + quarter.charCodeAt(1) + channel.length) % 5 !== 0) {
id++;
continue;
}
const base = 40 + ((id * 17) % 90);
rows.push({
id: `r${id}`,
region,
country,
category,
product,
channel,
year,
quarter,
sales: base * 100,
units: base,
cost: Math.round(base * 55),
});
id++;
}
}
}
}
}
}
}
return rows;
}

export const pivotRows: PivotFact[] = generatePivotRows();

export type PivotPreset = {
id: string;
label: string;
pivot: PivotConfig;
};

export const pivotPresets: PivotPreset[] = [
{
id: "region-quarter",
label: "Region × Quarter",
pivot: {
rows: ["region"],
columns: ["quarter"],
values: [{ accessor: "sales", aggregation: { type: "sum" } }],
},
},
{
id: "multi-rows",
label: "Region × Product",
pivot: {
rows: ["region", "product"],
columns: ["quarter"],
values: [{ accessor: "sales", aggregation: { type: "sum" } }],
},
},
{
id: "category-year-quarter",
label: "Category × Year → Quarter",
pivot: {
rows: ["category"],
columns: ["year", "quarter"],
values: [{ accessor: "sales", aggregation: { type: "sum" } }],
},
},
{
id: "channel-quarter",
label: "Channel × Quarter",
pivot: {
rows: ["channel"],
columns: ["quarter"],
values: [
{ accessor: "sales", aggregation: { type: "sum" }, label: "Sales" },
{ accessor: "units", aggregation: { type: "sum" }, label: "Units" },
],
},
},
{
id: "country-category",
label: "Country × Category",
pivot: {
rows: ["country"],
columns: ["category"],
values: [{ accessor: "sales", aggregation: { type: "average" } }],
showColumnTotals: false,
},
},
{
id: "values-only",
label: "Values only",
pivot: {
rows: ["region", "category"],
columns: [],
values: [
{ accessor: "sales", aggregation: { type: "sum" } },
{ accessor: "cost", aggregation: { type: "sum" } },
],
},
},
];

export const pivotConfig: PivotConfig = pivotPresets[0].pivot;

export const pivotDemoConfig = {
headers: pivotHeaders,
rows: pivotRows,
tableProps: { pivot: pivotConfig },
presets: pivotPresets,
};
Loading
Loading