Extension Components
Built-in components for building extension UI. These render natively within the extension target context.
AdminBlock
A container for admin-area extensions with an optional title.
import { reactExtension, ExtensionTarget } from "@qumra/riwaq";
import { AdminBlock, TextBlock, Heading } from "@qumra/riwaq";
export default reactExtension(
ExtensionTarget.AdminProductDetails,
(api) => (
<AdminBlock title="Product Analytics">
<Heading>Performance</Heading>
<TextBlock>Views this week: 142</TextBlock>
<TextBlock>Conversion rate: 3.2%</TextBlock>
</AdminBlock>
)
);
Props
| Prop | Type | Description |
|---|---|---|
title | string | Block title displayed in the header. |
children | ReactNode | Block content. |
AdminAction
A component for admin action extensions (buttons, panels).
import { reactExtension, ExtensionTarget } from "@qumra/riwaq";
import { AdminAction, TextBlock } from "@qumra/riwaq";
export default reactExtension(
ExtensionTarget.AdminAction,
(api) => (
<AdminAction title="Quick Edit">
<TextBlock>Edit product details inline.</TextBlock>
</AdminAction>
)
);
Props
| Prop | Type | Description |
|---|---|---|
title | string | Action title. |
children | ReactNode | Action content. |
TextBlock
Render a block of text content.
import { TextBlock } from "@qumra/riwaq";
<TextBlock>This is a paragraph of text content.</TextBlock>
<TextBlock emphasis="bold">Bold text</TextBlock>
<TextBlock emphasis="italic">Italic text</TextBlock>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
emphasis | "bold" | "italic" | -- | Text emphasis style. |
children | ReactNode | -- | Text content. |
Heading
A heading element for section titles.
import { Heading } from "@qumra/riwaq";
<Heading>Section Title</Heading>
<Heading level={2}>Subsection Title</Heading>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
level | 1 | 2 | 3 | 1 | Heading level. |
children | ReactNode | -- | Heading text. |
InlineLayout
Arrange items horizontally within an extension.
import { InlineLayout, TextBlock } from "@qumra/riwaq";
<InlineLayout columns={["fill", "fill"]}>
<TextBlock>Left column</TextBlock>
<TextBlock>Right column</TextBlock>
</InlineLayout>
<InlineLayout columns={["1fr", "2fr"]}>
<TextBlock>Sidebar</TextBlock>
<TextBlock>Main content</TextBlock>
</InlineLayout>
Props
| Prop | Type | Description |
|---|---|---|
columns | string[] | Column definitions (CSS grid values). |
children | ReactNode | Column content. |
BlockSpacer
Add vertical spacing between elements.
import { BlockSpacer, TextBlock } from "@qumra/riwaq";
<TextBlock>First paragraph</TextBlock>
<BlockSpacer size="large" />
<TextBlock>Second paragraph</TextBlock>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | "small" | "medium" | "large" | "medium" | Space size. |
Divider
A horizontal line separator.
import { Divider, TextBlock } from "@qumra/riwaq";
<TextBlock>Section 1</TextBlock>
<Divider />
<TextBlock>Section 2</TextBlock>
Full Example
extensions/product-insights/src/index.tsx
import {
reactExtension,
ExtensionTarget,
useApi,
useSettings,
useSessionToken,
} from "@qumra/riwaq";
import {
AdminBlock,
Heading,
TextBlock,
InlineLayout,
Divider,
BlockSpacer,
} from "@qumra/riwaq";
import { useState, useEffect } from "react";
export default reactExtension(
ExtensionTarget.AdminProductDetails,
() => <ProductInsights />
);
function ProductInsights() {
const api = useApi();
const settings = useSettings();
const sessionToken = useSessionToken();
const [stats, setStats] = useState(null);
useEffect(() => {
async function loadStats() {
const token = await sessionToken.get();
const res = await fetch(
`https://my-app.com/api/stats/${api.resource?.id}`,
{ headers: { Authorization: `Bearer ${token}` } }
);
setStats(await res.json());
}
if (api.resource?.id) loadStats();
}, [api.resource?.id]);
return (
<AdminBlock title={settings.blockTitle as string || "Product Insights"}>
<Heading level={2}>Performance</Heading>
<InlineLayout columns={["fill", "fill", "fill"]}>
<TextBlock>Views: {stats?.views ?? "..."}</TextBlock>
<TextBlock>Orders: {stats?.orders ?? "..."}</TextBlock>
<TextBlock>Revenue: {stats?.revenue ?? "..."}</TextBlock>
</InlineLayout>
<BlockSpacer size="large" />
<Divider />
<BlockSpacer size="small" />
<TextBlock>Store: {api.store.domain}</TextBlock>
</AdminBlock>
);
}