@timbal-ai/timbal-react 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +10 -0
- package/dist/app.cjs +126 -54
- package/dist/app.d.cts +2 -2
- package/dist/app.d.ts +2 -2
- package/dist/app.esm.js +1 -1
- package/dist/{chart-artifact-WDEW9dHT.d.cts → chart-artifact-C8-Py6lc.d.cts} +18 -1
- package/dist/{chart-artifact-Q5QgMtbj.d.ts → chart-artifact-CMnDys2t.d.ts} +18 -1
- package/dist/{chunk-YCXN67SD.esm.js → chunk-VOWNCS3F.esm.js} +96 -24
- package/dist/index.cjs +137 -65
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/package.json +1 -1
|
@@ -426,6 +426,15 @@ interface AppShellProps {
|
|
|
426
426
|
onNavOpenChange?: (open: boolean) => void;
|
|
427
427
|
className?: string;
|
|
428
428
|
mainClassName?: string;
|
|
429
|
+
/**
|
|
430
|
+
* Make the content region a bounded, non-scrolling flex column instead of the
|
|
431
|
+
* default padded scroll area. Use for full-bleed pages that own their own
|
|
432
|
+
* scroll — a full-page chat (`TimbalChat` / `Thread`), a canvas, a map, an
|
|
433
|
+
* editor — so a `h-full` / `flex-1 min-h-0` child fills exactly and a pinned
|
|
434
|
+
* footer (e.g. the chat composer) stays put instead of riding down on scroll.
|
|
435
|
+
* Do **not** combine with `h-[calc(100dvh-…)]` guesses on the child.
|
|
436
|
+
*/
|
|
437
|
+
contentFill?: boolean;
|
|
429
438
|
}
|
|
430
439
|
/**
|
|
431
440
|
* App-first layout: sidebar + topbar + main, with optional **floating** copilot.
|
|
@@ -557,7 +566,8 @@ type AppDensityClassKey = keyof typeof APP_DENSITY_CLASSES;
|
|
|
557
566
|
declare function appDensityClass(key: AppDensityClassKey, density?: AppDensity): string;
|
|
558
567
|
|
|
559
568
|
interface PageHeaderProps {
|
|
560
|
-
title
|
|
569
|
+
/** Page title. Omit for a headerless page (no `<h1>` rendered). */
|
|
570
|
+
title?: ReactNode;
|
|
561
571
|
description?: ReactNode;
|
|
562
572
|
actions?: ReactNode;
|
|
563
573
|
className?: string;
|
|
@@ -573,6 +583,13 @@ interface PageProps extends PageHeaderProps {
|
|
|
573
583
|
* `compact` tightens page insets, section gaps, and card padding.
|
|
574
584
|
*/
|
|
575
585
|
density?: AppDensity;
|
|
586
|
+
/**
|
|
587
|
+
* Make the page a bounded, full-height flex column instead of a centered,
|
|
588
|
+
* content-sized column. Pair with `AppShell contentFill` for full-bleed pages
|
|
589
|
+
* (a full-page chat, a canvas, an editor) whose body should fill the viewport
|
|
590
|
+
* and own its own scroll. Give the fill child `min-h-0 flex-1`.
|
|
591
|
+
*/
|
|
592
|
+
fill?: boolean;
|
|
576
593
|
className?: string;
|
|
577
594
|
}
|
|
578
595
|
declare const Page: FC<PageProps>;
|
|
@@ -239,6 +239,40 @@ You are **not** required to copy any example layout, page title, section order,
|
|
|
239
239
|
|
|
240
240
|
When in doubt: compose from the **component menu** + **guidelines**, then adapt creatively to the request.
|
|
241
241
|
|
|
242
|
+
### Layout archetypes \u2014 pick the shape that fits (don't default to one)
|
|
243
|
+
|
|
244
|
+
The most common failure is shipping the **same** layout every time: sidebar + topbar + \`Page\` + one \`MetricRow\` + one full-width \`DataTable\`. That is *one* archetype, not *the* layout. Choose deliberately \u2014 different domains want different shapes, and varying the shell/page composition is encouraged.
|
|
245
|
+
|
|
246
|
+
| Archetype | When | Compose |
|
|
247
|
+
|-----------|------|---------|
|
|
248
|
+
| **Sidebar dashboard** | Multi-section product (CRM, billing, ops) with nav | \`StudioSidebar\` in \`AppShell.sidebar\` + \`Page\` \u2192 \`Section\` |
|
|
249
|
+
| **Focused / no-chrome** | A single tool or one-screen utility | \`AppShell\` (no sidebar) + \`Page\` (optionally just \`AppShellTopbar\`); or a centered narrow column |
|
|
250
|
+
| **Bento overview** | Home / at-a-glance dashboards | \`Page\` + an **asymmetric grid** of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` spanning different widths (not a uniform row + table) |
|
|
251
|
+
| **Split master\u2013detail** | Inbox, triage queue, record browser, log explorer | \`AppShell contentFill\` + \`Page fill\` + a two-column flex row, each pane \`min-h-0 overflow-y-auto\` |
|
|
252
|
+
| **Full-page chat / canvas** | Chat-first app, editor, map, single full-bleed surface | \`AppShell contentFill\` + headerless \`Page fill\` + a \`min-h-0 flex-1\` child (e.g. \`TimbalChat\`) |
|
|
253
|
+
| **Copilot overlay** | A data app that also wants an assistant | any of the above + \`AppShell chat={<AppChatPanel />}\` (floating, never a second column) |
|
|
254
|
+
| **Section-switcher** | One page, several views | \`SubNav\` / \`PillSegmentedTabs\` (\`trackVariant="flush"\`) switching panels with state/router |
|
|
255
|
+
|
|
256
|
+
Mix them: vary the grid columns, density, header placement (\`Page\` actions vs. a global \`AppShellTopbar\`), and whether there's a sidebar at all. Two dashboards for two domains should not look identical.
|
|
257
|
+
|
|
258
|
+
### Full-height pages (chat, canvas, split views)
|
|
259
|
+
|
|
260
|
+
The content region is a **padded scroll area** by default \u2014 great for stacked \`Page\` \u2192 \`Section\` content, wrong for a surface that must fill the viewport. For full-bleed pages:
|
|
261
|
+
|
|
262
|
+
- Pass **\`contentFill\`** to \`AppShell\` \u2192 the content region becomes a bounded, non-scrolling flex column (clipped, no bottom padding).
|
|
263
|
+
- Pass **\`fill\`** to \`Page\` \u2192 the page becomes a \`min-h-0 flex-1\` flex column.
|
|
264
|
+
- Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
|
|
265
|
+
|
|
266
|
+
\`\`\`tsx
|
|
267
|
+
<AppShell contentFill topbar={<AppShellTopbar actions={<ModeToggle />} />}>
|
|
268
|
+
<Page fill> {/* headerless: omit title */}
|
|
269
|
+
<TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
|
|
270
|
+
</Page>
|
|
271
|
+
</AppShell>
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
**Don't** size full-height content with \`h-[calc(100dvh-\u2026)]\` (guesses chrome height \u2192 spurious scrollbar) or \`min-h-[\u2026]\` (free-growing floor \u2192 a pinned footer like the chat composer rides down on scroll). Let \`contentFill\` + \`fill\` provide the bounded height. \`Page\` with no \`title\` renders **no header** \u2014 you don't need to abandon \`Page\` to drop a heading.
|
|
275
|
+
|
|
242
276
|
### Module layout (source folders)
|
|
243
277
|
|
|
244
278
|
Presentational groups \u2014 import from the package root, not from these paths:
|
|
@@ -297,12 +331,12 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
297
331
|
|
|
298
332
|
| Component | Use for |
|
|
299
333
|
|-----------|---------|
|
|
300
|
-
| \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen
|
|
334
|
+
| \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen\`, **\`contentFill\`** (bounded non-scrolling content region for full-bleed pages \u2014 chat/canvas/split view). |
|
|
301
335
|
| \`AppShellTopbar\` | Full-width top bar: \`start\`, \`actions\` slots. |
|
|
302
336
|
| \`AppCopilotProvider\` | React context for copilot-aware tools (page, filters, selection, etc.). |
|
|
303
337
|
| \`AppChatPanel\` | Floating thread: \`workforceId\`, \`welcome\`, \`debug\`. |
|
|
304
338
|
| \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
|
|
305
|
-
| \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, \`density\` (\`"default"\` | \`"compact"\`), children. |
|
|
339
|
+
| \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, \`density\` (\`"default"\` | \`"compact"\`), children. **\`title\` is optional** \u2014 omit it for a headerless page (no \`<h1>\`). **\`fill\`** makes it a \`min-h-0 flex-1\` column for full-height content (pair with \`AppShell contentFill\`). |
|
|
306
340
|
| \`Section\` | Titled block inside a page. Optional \`density\` overrides inherited page density. |
|
|
307
341
|
| \`SubNav\` | **Section switcher** (Overview / Reports pill bar): \`items\`, \`activeId\`, \`onChange\`. Never use Radix/shadcn \`Tabs\` \u2014 it is not in this package. Switch panels with state or the router. |
|
|
308
342
|
| **Menus** | **Select** = short list, no search. **Combobox** = searchable (same trigger as Select). **Command** only inside \`PopoverContent variant="list"\` or Combobox \u2014 never padded default Popover. See \`examples/app-kit/src/recipes/primitives-catalog.ts\`. |
|
|
@@ -395,8 +429,11 @@ Ready-made **section patterns** assembled from the components above. Each is a c
|
|
|
395
429
|
- **Empty states** \u2014 no-data / no-results / first-run. Compose \`EmptyState\` + \`Card\` + \`Button\`.
|
|
396
430
|
- **Sign-in card** \u2014 centered auth entry. Compose \`Card\` + \`Input\` + \`Label\` + \`Button\`.
|
|
397
431
|
|
|
398
|
-
**Shells &
|
|
432
|
+
**Shells & layouts**
|
|
399
433
|
- **Minimal shell** \u2014 \`AppShell\` + \`Page\` (no sidebar/chat).
|
|
434
|
+
- **Bento dashboard** \u2014 \`Page\` + an asymmetric grid of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` (varied spans) for overview/home screens.
|
|
435
|
+
- **Split view** \u2014 master\u2013detail: \`AppShell contentFill\` + \`Page fill\` + a two-pane flex row (list + detail), each pane \`min-h-0 overflow-y-auto\`.
|
|
436
|
+
- **Full-page chat** \u2014 \`AppShell contentFill\` + headerless \`Page fill\` + \`TimbalChat className="min-h-0 flex-1"\` (composer pinned; no \`h-[calc(...)]\`).
|
|
400
437
|
- **Copilot overlay** \u2014 \`AppShell\` + floating \`AppChatPanel\`.
|
|
401
438
|
- **Theme presets** \u2014 apply a brand preset programmatically (\`applyThemePreset\` / \`applyTimbalTheme\`); never hand-author OKLCH and don't expose a theme picker to end users.
|
|
402
439
|
|
|
@@ -1600,6 +1637,7 @@ var AppShellBody = ({
|
|
|
1600
1637
|
sidebar,
|
|
1601
1638
|
topbarContent,
|
|
1602
1639
|
mainClassName,
|
|
1640
|
+
contentFill = false,
|
|
1603
1641
|
insetPaddingPx,
|
|
1604
1642
|
insetExpanded,
|
|
1605
1643
|
children
|
|
@@ -1622,7 +1660,10 @@ var AppShellBody = ({
|
|
|
1622
1660
|
"div",
|
|
1623
1661
|
{
|
|
1624
1662
|
className: cn(
|
|
1625
|
-
"aui-app-shell-scroll flex min-h-0 flex-1 flex-col
|
|
1663
|
+
"aui-app-shell-scroll flex min-h-0 flex-1 flex-col",
|
|
1664
|
+
// Padded scroll region by default; a full-bleed page (chat / canvas) owns
|
|
1665
|
+
// its own scroll, so clip here and let the bounded `main` fill exactly.
|
|
1666
|
+
contentFill ? "overflow-hidden" : "overflow-y-auto",
|
|
1626
1667
|
!topbarContent && appShellInsetTopClass
|
|
1627
1668
|
),
|
|
1628
1669
|
children: [
|
|
@@ -1631,8 +1672,11 @@ var AppShellBody = ({
|
|
|
1631
1672
|
"main",
|
|
1632
1673
|
{
|
|
1633
1674
|
className: cn(
|
|
1634
|
-
|
|
1635
|
-
|
|
1675
|
+
// Bounded flex column by default so `h-full` / `flex-1 min-h-0` children
|
|
1676
|
+
// (full-page chat, canvas) resolve a height without `mainClassName` surgery.
|
|
1677
|
+
"aui-app-shell-main flex min-h-0 min-w-0 flex-1 flex-col",
|
|
1678
|
+
// Bottom breathing room for scrolling content; full-bleed pages skip it.
|
|
1679
|
+
!contentFill && appShellInsetBottomClass,
|
|
1636
1680
|
mainClassName
|
|
1637
1681
|
),
|
|
1638
1682
|
children
|
|
@@ -1662,7 +1706,8 @@ var AppShell = ({
|
|
|
1662
1706
|
defaultNavOpen = false,
|
|
1663
1707
|
onNavOpenChange,
|
|
1664
1708
|
className,
|
|
1665
|
-
mainClassName
|
|
1709
|
+
mainClassName,
|
|
1710
|
+
contentFill = false
|
|
1666
1711
|
}) => {
|
|
1667
1712
|
const topbarContent = topbar ?? header;
|
|
1668
1713
|
const hasChat = Boolean(chat);
|
|
@@ -1709,6 +1754,7 @@ var AppShell = ({
|
|
|
1709
1754
|
sidebar,
|
|
1710
1755
|
topbarContent,
|
|
1711
1756
|
mainClassName,
|
|
1757
|
+
contentFill,
|
|
1712
1758
|
insetPaddingPx,
|
|
1713
1759
|
insetExpanded,
|
|
1714
1760
|
children
|
|
@@ -1858,11 +1904,14 @@ var PageHeader = ({
|
|
|
1858
1904
|
className
|
|
1859
1905
|
}) => {
|
|
1860
1906
|
const pageHeaderClass = useAppDensityClass("pageHeader");
|
|
1907
|
+
if (title == null && description == null && actions == null) {
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1861
1910
|
return /* @__PURE__ */ jsxs7("header", { className: cn("aui-app-page-header", pageHeaderClass, className), children: [
|
|
1862
|
-
/* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
1863
|
-
/* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
1911
|
+
title != null || description != null ? /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
1912
|
+
title != null ? /* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }) : null,
|
|
1864
1913
|
description ? /* @__PURE__ */ jsx10("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
|
|
1865
|
-
] }),
|
|
1914
|
+
] }) : null,
|
|
1866
1915
|
actions ? /* @__PURE__ */ jsx10("div", { className: "aui-app-page-header-actions flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
|
|
1867
1916
|
] });
|
|
1868
1917
|
};
|
|
@@ -1872,6 +1921,7 @@ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
|
1872
1921
|
var PageFrame = ({
|
|
1873
1922
|
children,
|
|
1874
1923
|
breadcrumbs,
|
|
1924
|
+
fill = false,
|
|
1875
1925
|
className,
|
|
1876
1926
|
...headerProps
|
|
1877
1927
|
}) => {
|
|
@@ -1880,7 +1930,11 @@ var PageFrame = ({
|
|
|
1880
1930
|
return /* @__PURE__ */ jsxs8(
|
|
1881
1931
|
"div",
|
|
1882
1932
|
{
|
|
1883
|
-
className: cn(
|
|
1933
|
+
className: cn(
|
|
1934
|
+
"aui-app-page",
|
|
1935
|
+
fill ? "flex min-h-0 min-w-0 flex-1 flex-col" : pageColumnClass,
|
|
1936
|
+
className
|
|
1937
|
+
),
|
|
1884
1938
|
"data-density": density,
|
|
1885
1939
|
children: [
|
|
1886
1940
|
breadcrumbs,
|
|
@@ -1894,10 +1948,20 @@ var Page = ({
|
|
|
1894
1948
|
density = "default",
|
|
1895
1949
|
children,
|
|
1896
1950
|
breadcrumbs,
|
|
1951
|
+
fill = false,
|
|
1897
1952
|
className,
|
|
1898
1953
|
...headerProps
|
|
1899
1954
|
}) => {
|
|
1900
|
-
return /* @__PURE__ */ jsx11(AppDensityProvider, { density, children: /* @__PURE__ */ jsx11(
|
|
1955
|
+
return /* @__PURE__ */ jsx11(AppDensityProvider, { density, children: /* @__PURE__ */ jsx11(
|
|
1956
|
+
PageFrame,
|
|
1957
|
+
{
|
|
1958
|
+
breadcrumbs,
|
|
1959
|
+
fill,
|
|
1960
|
+
className,
|
|
1961
|
+
...headerProps,
|
|
1962
|
+
children
|
|
1963
|
+
}
|
|
1964
|
+
) });
|
|
1901
1965
|
};
|
|
1902
1966
|
|
|
1903
1967
|
// src/app/layout/Section.tsx
|
|
@@ -2810,6 +2874,7 @@ var Breadcrumbs = ({ items, className }) => {
|
|
|
2810
2874
|
};
|
|
2811
2875
|
|
|
2812
2876
|
// src/app/forms/Field.tsx
|
|
2877
|
+
import { useId as useId4 } from "react";
|
|
2813
2878
|
import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2814
2879
|
var Field = ({
|
|
2815
2880
|
label,
|
|
@@ -2835,7 +2900,8 @@ var FieldInput = ({
|
|
|
2835
2900
|
id,
|
|
2836
2901
|
...inputProps
|
|
2837
2902
|
}) => {
|
|
2838
|
-
const
|
|
2903
|
+
const autoId = useId4();
|
|
2904
|
+
const inputId = id ?? inputProps.name ?? autoId;
|
|
2839
2905
|
return /* @__PURE__ */ jsx36(
|
|
2840
2906
|
Field,
|
|
2841
2907
|
{
|
|
@@ -2858,6 +2924,7 @@ var FieldInput = ({
|
|
|
2858
2924
|
};
|
|
2859
2925
|
|
|
2860
2926
|
// src/app/forms/FieldTextarea.tsx
|
|
2927
|
+
import { useId as useId5 } from "react";
|
|
2861
2928
|
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
2862
2929
|
var textareaClass = cn(
|
|
2863
2930
|
appInputClass,
|
|
@@ -2872,7 +2939,8 @@ var FieldTextarea = ({
|
|
|
2872
2939
|
id,
|
|
2873
2940
|
...props
|
|
2874
2941
|
}) => {
|
|
2875
|
-
const
|
|
2942
|
+
const autoId = useId5();
|
|
2943
|
+
const textareaId = id ?? props.name ?? autoId;
|
|
2876
2944
|
return /* @__PURE__ */ jsx37(
|
|
2877
2945
|
Field,
|
|
2878
2946
|
{
|
|
@@ -2895,6 +2963,7 @@ var FieldTextarea = ({
|
|
|
2895
2963
|
};
|
|
2896
2964
|
|
|
2897
2965
|
// src/app/forms/FieldSelect.tsx
|
|
2966
|
+
import { useId as useId6 } from "react";
|
|
2898
2967
|
import { ChevronDownIcon } from "lucide-react";
|
|
2899
2968
|
import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2900
2969
|
var selectWrapClass = "relative";
|
|
@@ -2912,7 +2981,8 @@ var FieldSelect = ({
|
|
|
2912
2981
|
id,
|
|
2913
2982
|
...props
|
|
2914
2983
|
}) => {
|
|
2915
|
-
const
|
|
2984
|
+
const autoId = useId6();
|
|
2985
|
+
const selectId = id ?? props.name ?? autoId;
|
|
2916
2986
|
return /* @__PURE__ */ jsx38(
|
|
2917
2987
|
Field,
|
|
2918
2988
|
{
|
|
@@ -2945,6 +3015,7 @@ var FieldSelect = ({
|
|
|
2945
3015
|
};
|
|
2946
3016
|
|
|
2947
3017
|
// src/app/forms/FieldSwitch.tsx
|
|
3018
|
+
import { useId as useId7 } from "react";
|
|
2948
3019
|
import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2949
3020
|
var trackClass = cn(
|
|
2950
3021
|
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
|
|
@@ -2965,7 +3036,8 @@ var FieldSwitch = ({
|
|
|
2965
3036
|
id,
|
|
2966
3037
|
...props
|
|
2967
3038
|
}) => {
|
|
2968
|
-
const
|
|
3039
|
+
const autoId = useId7();
|
|
3040
|
+
const inputId = id ?? props.name ?? autoId;
|
|
2969
3041
|
return /* @__PURE__ */ jsxs30(
|
|
2970
3042
|
"label",
|
|
2971
3043
|
{
|
|
@@ -3430,7 +3502,7 @@ function DataTable({
|
|
|
3430
3502
|
}
|
|
3431
3503
|
|
|
3432
3504
|
// src/app/data/ChartPanel.tsx
|
|
3433
|
-
import { useId as
|
|
3505
|
+
import { useId as useId8 } from "react";
|
|
3434
3506
|
import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
3435
3507
|
var ChartPanel = ({
|
|
3436
3508
|
title,
|
|
@@ -3446,7 +3518,7 @@ var ChartPanel = ({
|
|
|
3446
3518
|
const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
|
|
3447
3519
|
const metricChartPlotRegionClass = useAppDensityClass("metricChartPlotRegion");
|
|
3448
3520
|
const chartPanelBodyClass = useAppDensityClass("chartPanelBody");
|
|
3449
|
-
const titleId =
|
|
3521
|
+
const titleId = useId8();
|
|
3450
3522
|
const resolvedTitle = title ?? artifact?.title;
|
|
3451
3523
|
const hasHeader = Boolean(resolvedTitle || description || actions);
|
|
3452
3524
|
const body = loading ? /* @__PURE__ */ jsx45(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ jsx45(ChartArtifactView, { artifact, embedded: true, height }) : null);
|
|
@@ -3489,7 +3561,7 @@ var ChartPanel = ({
|
|
|
3489
3561
|
};
|
|
3490
3562
|
|
|
3491
3563
|
// src/app/data/MetricRow.tsx
|
|
3492
|
-
import { useId as
|
|
3564
|
+
import { useId as useId9, useState as useState5 } from "react";
|
|
3493
3565
|
import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
3494
3566
|
var MetricRow = ({
|
|
3495
3567
|
title,
|
|
@@ -3504,7 +3576,7 @@ var MetricRow = ({
|
|
|
3504
3576
|
className
|
|
3505
3577
|
}) => {
|
|
3506
3578
|
const metricTileClass = useAppDensityClass("metricTile");
|
|
3507
|
-
const titleId =
|
|
3579
|
+
const titleId = useId9();
|
|
3508
3580
|
const selectable = onMetricChange != null || activeMetricId != null;
|
|
3509
3581
|
const [internalId, setInternalId] = useState5(
|
|
3510
3582
|
defaultActiveMetricId ?? metrics[0]?.id
|
|
@@ -3573,7 +3645,7 @@ var MetricRow = ({
|
|
|
3573
3645
|
};
|
|
3574
3646
|
|
|
3575
3647
|
// src/app/data/MetricChartCard.tsx
|
|
3576
|
-
import { useId as
|
|
3648
|
+
import { useId as useId10, useState as useState6 } from "react";
|
|
3577
3649
|
import { jsx as jsx47, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
3578
3650
|
var MetricChartCard = ({
|
|
3579
3651
|
title,
|
|
@@ -3597,7 +3669,7 @@ var MetricChartCard = ({
|
|
|
3597
3669
|
const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
|
|
3598
3670
|
const metricChartRegionClass = useAppDensityClass("metricChartRegion");
|
|
3599
3671
|
const metricTileClass = useAppDensityClass("metricTile");
|
|
3600
|
-
const titleId =
|
|
3672
|
+
const titleId = useId10();
|
|
3601
3673
|
const [internalId, setInternalId] = useState6(
|
|
3602
3674
|
defaultActiveMetricId ?? metrics[0]?.id
|
|
3603
3675
|
);
|
|
@@ -3794,7 +3866,7 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3794
3866
|
}
|
|
3795
3867
|
|
|
3796
3868
|
// src/charts/sparkline.tsx
|
|
3797
|
-
import { useId as
|
|
3869
|
+
import { useId as useId11 } from "react";
|
|
3798
3870
|
import { Fragment as Fragment6, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3799
3871
|
var Sparkline = ({
|
|
3800
3872
|
data,
|
|
@@ -3807,7 +3879,7 @@ var Sparkline = ({
|
|
|
3807
3879
|
className,
|
|
3808
3880
|
ariaLabel = "Trend"
|
|
3809
3881
|
}) => {
|
|
3810
|
-
const uid =
|
|
3882
|
+
const uid = useId11();
|
|
3811
3883
|
const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
|
|
3812
3884
|
if (values.length === 0) {
|
|
3813
3885
|
return /* @__PURE__ */ jsx48("span", { className: cn("inline-block", className), style: { width, height } });
|