React SDK
@concepttocloud/saiku-embed-react ist ein typisierter
React-Wrapper um das bestehende
<saiku-embed>-Custom-Element. Das Element funktioniert
bereits in React — React 18+ leitet unbekannte Attribute direkt an das
DOM durch — aber React-Teams bewerten anhand von npm install +
typisierten Imports, nicht <script>-Tags. Dieses SDK schließt diese
Lücke, ohne die zugrundeliegende Runtime zu ändern.
Ausgeliefert in saiku v4.7 als saiku#1432.
Installation
npm install @concepttocloud/saiku-embed-react reactDas Paket hat eine Peer-Abhängigkeit von React 18+. Das Basis-Paket
@concepttocloud/saiku-embed ist eine Runtime-Abhängigkeit und wird
automatisch mit hereingezogen.
Verwendung
import { SaikuEmbed } from "@concepttocloud/saiku-embed-react";
function Dashboard({ token }: { token: string }) { return ( <SaikuEmbed server="https://YOUR-WORKSPACE.saiku.bi" token={token} path="homes/admin/Sales.saiku" render="chart" mode="bar" height="480px" /> );}Der Import des Pakets hat den Nebeneffekt, dass das zugrundeliegende
Custom-Element registriert wird — Sie brauchen keinen separaten
import "@concepttocloud/saiku-embed", es sei denn, Sie möchten das Tag
auch außerhalb des React-Baums verfügbar haben.
Props
| Prop | Typ | Standard | Anmerkungen |
|---|---|---|---|
server | string | optional | Origin des Saiku-Launchers. Weglassen für Same-Origin-Embeds. |
path | string | erforderlich | Query-Pfad / Dashboard-Pfad / Cube-Ref, abhängig von kind. |
kind | "query" | "dashboard" | "ai" | "query" | Wählt die Embed-Variante. |
token | string | optional | Serverseitig geminteter Embed-Token. Weglassen für öffentliche Grants. |
render | "table" | "matrix" | "chart" | "table" | Nur bei kind="query" bedeutsam. |
mode | "bar" | "line" | "pie" | "bar" | Nur bei render="chart" bedeutsam. |
height | string | "400px" | CSS-Höhe der gerenderten Oberfläche. |
style | React.CSSProperties | optional | Standard-React-Style-Prop. |
className | string | optional | Standard-React-className-Prop. |
id | string | optional | Durchgereicht für e2e-Selektoren. |
data-testid | string | optional | Durchgereicht an den DOM-Knoten. |
Vollständige Typdeklarationen werden in der index.d.ts des Pakets
ausgeliefert.
Kinds
Gespeicherte Query als Tabelle
<SaikuEmbed server="https://saiku.example.com" token={token} path="homes/admin/Sales.saiku" height="400px"/>Chart
<SaikuEmbed server="https://saiku.example.com" token={token} path="homes/admin/Sales.saiku" render="chart" mode="bar" height="500px"/>Matrix
Der Matrix-Modus bewahrt die Zeilen- / Spaltenachsen-Struktur — Measures
auf Spalten, Dimension-Members auf Zeilen — statt zu einer einzelnen
Row-Key-Map zu flatten, wie es render="table" tut. Nützlich für
Pivot-Style-Reports.
<SaikuEmbed server="https://saiku.example.com" token={token} path="homes/admin/Sales.saiku" render="matrix" height="500px"/>AI-Ask-Widget
Zeigen Sie den Token auf einen Cube (statt auf eine gespeicherte Query) und setzen Sie eine natürlichsprachliche Ask-Box hinein. Erfordert einen AI-kind-Embed-Token und einen Launcher mit einem konfigurierten LLM-Provider.
<SaikuEmbed server="https://saiku.example.com" token={aiToken} kind="ai" path="foodmart/FoodMart/FoodMart/Sales" height="240px"/>Gespeichertes Dashboard
<SaikuEmbed server="https://saiku.example.com" token={token} kind="dashboard" path="homes/admin/exec.saikudash" height="700px"/>Anonymes öffentliches Embed
Wenn die Ressource auf dem Server als öffentlich einbettbar markiert ist (siehe Öffentliche Embeds), lassen Sie den Token ganz weg:
<SaikuEmbed server="https://saiku.example.com" path="shared/public-chart.saiku" render="chart"/>Einen Token von Ihrem Server minten
mintEmbedToken() ist ein Node- / Edge-Function-Helfer für den sehr
gängigen Fall, im Namen eines Endnutzers einen Embed-Token zu minten,
bevor <SaikuEmbed> gerendert wird.
import { mintEmbedToken } from "@concepttocloud/saiku-embed-react";
export async function POST(request: Request) { const auth = "Basic " + Buffer.from( `${process.env.SAIKU_USER}:${process.env.SAIKU_PASS}`, ).toString("base64");
const { token, expiresAt } = await mintEmbedToken({ server: process.env.SAIKU_URL!, authorization: auth, resourceKind: "query", resourcePath: "homes/admin/Sales.saiku", ttlHours: 24, label: "Public marketing page", });
return Response.json({ token, expiresAt });}import { mintEmbedToken } from "@concepttocloud/saiku-embed-react";
export async function loader() { const auth = "Basic " + Buffer.from( `${process.env.SAIKU_USER}:${process.env.SAIKU_PASS}`, ).toString("base64");
const { token } = await mintEmbedToken({ server: process.env.SAIKU_URL!, authorization: auth, resourceKind: "query", resourcePath: "homes/admin/Sales.saiku", });
return { token };}import { mintEmbedToken } from "@concepttocloud/saiku-embed-react";
export default { async fetch(request: Request, env: Env) { const auth = "Basic " + btoa(`${env.SAIKU_USER}:${env.SAIKU_PASS}`); const { token } = await mintEmbedToken({ server: env.SAIKU_URL, authorization: auth, resourceKind: "query", resourcePath: "homes/admin/Sales.saiku", fetch: fetch.bind(globalThis), }); return Response.json({ token }); },};Clientseitig landet der Token im token-Prop:
"use client";import useSWR from "swr";import { SaikuEmbed } from "@concepttocloud/saiku-embed-react";
export default function EmbedTile() { const { data } = useSWR("/api/embed-token", (u) => fetch(u, { method: "POST" }).then((r) => r.json())); if (!data?.token) return <div>Loading…</div>; return <SaikuEmbed server="…" token={data.token} path="homes/admin/Sales.saiku" />;}mintEmbedToken-Optionen
| Option | Typ | Anmerkungen |
|---|---|---|
server | string | Basis-URL des Launchers. |
authorization | string | Wert für den Authorization-Header (Basic … oder Bearer …). |
resourceKind | "query" | "dashboard" | "ai" | Art der Ressource, die der Token anpinnt. |
resourcePath | string | Pfad (für query/dashboard) oder Cube-Ref (für ai). |
ttlHours | number (optional) | Token-Lebensdauer; Server-Standard ist 72 h. |
label | string (optional) | Menschenlesbares Label, das die Admin-UI neben dem Token zeigt. |
fetch | typeof fetch (optional) | Überschreibbar für Tests + Nicht-Browser-Runtimes (z. B. Cloudflare Workers). |
Gibt { token, expiresAt } zurück. Wirft, wenn der Server Nicht-2xx
zurückgibt oder der Antwort-Body kein Token-Umschlag ist.
Autocomplete auf dem rohen Tag
Das Paket erweitert sowohl das globale JSX.IntrinsicElements
(React 17/18) als auch React.JSX.IntrinsicElements (React 19+), sodass
das rohe Custom-Element denselben typisierten Prop-Satz wie
<SaikuEmbed> bekommt. Verwenden Sie, was Sie bevorzugen:
// Typed React component<SaikuEmbed server="…" token={token} path="…" render="chart" />
// Or the raw custom element (also typed)<saiku-embed server="…" token={token} path="…" render="chart" />Theming
Das Embed lebt innerhalb eines Shadow-Roots, sodass CSS der Host-Seite nicht hineinlecken kann. Farbanpassung über CSS-Custom-Properties auf dem Wrapper:
<SaikuEmbed server="…" token={token} path="…" style={{ "--saiku-embed-fg": "#0f172a", "--saiku-embed-bg": "transparent", "--saiku-embed-border": "#cbd5e1", "--saiku-embed-header-bg": "#f1f5f9", "--saiku-embed-tile-bg": "#ffffff", "--saiku-embed-row-hover": "#e2e8f0", "--saiku-embed-negative": "#b91c1c", } as React.CSSProperties}/>Die vollständige Liste der themebaren Variablen ist auf der Basis-Embed-Seite.
Version-Pinning
Die Version des React-SDK folgt der Basis-Release von
@concepttocloud/saiku-embed eins zu eins. 3.19.0 des SDK verwendet
3.19.0 der Basis-Runtime — die Runtime-Abhängigkeit wird zum
Release-Zeitpunkt gepinnt, sodass ein Konsument niemals versehentlich
Majors über die beiden hinweg mischen kann.
Bundle-Größe
- Wrapper: ~1 KB gzipped (die gesamte Runtime ist ein einzelner
React.createElement-Aufruf). - Basis-Custom-Element: ~213 KB gzipped (Svelte-5-CE-Runtime + ECharts + die Embed-Renderer).
- React: Peer-Dependency — wird gegen keines der beiden Pakete gezählt.
Nicht-Ziele für v1
- React-Hooks für Query-Ergebnisse (
useSaikuQuery) — das Custom-Element verwaltet seinen eigenen State; ein typisierter Data-Hook dupliziert die AI Query API-Oberfläche. Follow-up, falls jemand fragt. - Server-Component-Variante —
<SaikuEmbed>ist nur im Browser (es rendert einen Shadow-Root). Serverseitig gerenderte initiale Daten für Hydration sind ein Follow-up, falls sich das Muster als schmerzhaft erweist. - Bundle-Splitting pro Kind — der aktuelle Wrapper zieht immer das volle Basis-Bundle. Tree-Shaking nach Kind ist eine Änderung im Basis-Paket, keine Wrapper-Änderung.