@stackframe/dashboard-ui-components 2.8.86 → 2.8.89
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/dist/components/analytics-chart/analytics-chart-pie.js +3 -3
- package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/components/data-grid/data-grid-sizing.d.ts +2 -1
- package/dist/components/data-grid/data-grid-sizing.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid-sizing.js +33 -4
- package/dist/components/data-grid/data-grid-sizing.js.map +1 -1
- package/dist/components/data-grid/data-grid-toolbar.js +18 -15
- package/dist/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/components/data-grid/data-grid.d.ts +35 -1
- package/dist/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid.js +329 -127
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/data-grid.test.d.ts +1 -0
- package/dist/components/data-grid/data-grid.test.js +215 -0
- package/dist/components/data-grid/data-grid.test.js.map +1 -0
- package/dist/components/data-grid/index.d.ts +3 -2
- package/dist/components/data-grid/index.js +13 -0
- package/dist/components/data-grid/state.d.ts.map +1 -1
- package/dist/components/data-grid/state.js +24 -7
- package/dist/components/data-grid/state.js.map +1 -1
- package/dist/components/data-grid/types.d.ts +34 -3
- package/dist/components/data-grid/types.d.ts.map +1 -1
- package/dist/components/data-grid/use-data-source.d.ts +6 -0
- package/dist/components/data-grid/use-data-source.d.ts.map +1 -1
- package/dist/components/data-grid/use-data-source.js +10 -2
- package/dist/components/data-grid/use-data-source.js.map +1 -1
- package/dist/components/tabs.d.ts +5 -1
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +40 -27
- package/dist/components/tabs.js.map +1 -1
- package/dist/dashboard-ui-components.global.js +672 -368
- package/dist/dashboard-ui-components.global.js.map +4 -4
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js +3 -3
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid-sizing.d.ts +2 -1
- package/dist/esm/components/data-grid/data-grid-sizing.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid-sizing.js +33 -5
- package/dist/esm/components/data-grid/data-grid-sizing.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid-toolbar.js +18 -15
- package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.d.ts +35 -1
- package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid.js +329 -128
- package/dist/esm/components/data-grid/data-grid.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.test.d.ts +1 -0
- package/dist/esm/components/data-grid/data-grid.test.js +215 -0
- package/dist/esm/components/data-grid/data-grid.test.js.map +1 -0
- package/dist/esm/components/data-grid/index.d.ts +3 -2
- package/dist/esm/components/data-grid/index.js +3 -2
- package/dist/esm/components/data-grid/state.d.ts.map +1 -1
- package/dist/esm/components/data-grid/state.js +24 -7
- package/dist/esm/components/data-grid/state.js.map +1 -1
- package/dist/esm/components/data-grid/types.d.ts +34 -3
- package/dist/esm/components/data-grid/types.d.ts.map +1 -1
- package/dist/esm/components/data-grid/use-data-source.d.ts +6 -0
- package/dist/esm/components/data-grid/use-data-source.d.ts.map +1 -1
- package/dist/esm/components/data-grid/use-data-source.js +10 -2
- package/dist/esm/components/data-grid/use-data-source.js.map +1 -1
- package/dist/esm/components/tabs.d.ts +5 -1
- package/dist/esm/components/tabs.d.ts.map +1 -1
- package/dist/esm/components/tabs.js +40 -27
- package/dist/esm/components/tabs.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/package.json +4 -4
|
@@ -98,7 +98,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
98
98
|
const activeIdx = hoverKey ? legendRows.findIndex((r) => r.key === hoverKey) : -1;
|
|
99
99
|
return /* @__PURE__ */ jsxs("div", {
|
|
100
100
|
ref: wrapperRef,
|
|
101
|
-
className: "relative w-full select-none",
|
|
101
|
+
className: "relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col select-none",
|
|
102
102
|
onClick: (e) => e.stopPropagation(),
|
|
103
103
|
children: [zoomRange && /* @__PURE__ */ jsx("div", {
|
|
104
104
|
className: "absolute right-2 top-2 z-20",
|
|
@@ -113,7 +113,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
113
113
|
}), /* @__PURE__ */ jsx("span", { children: strings.resetZoom })]
|
|
114
114
|
})
|
|
115
115
|
}), /* @__PURE__ */ jsxs("div", {
|
|
116
|
-
className: "flex min-h-
|
|
116
|
+
className: "flex h-full min-h-0 w-full flex-1 flex-wrap content-center items-center justify-center gap-x-10 gap-y-6",
|
|
117
117
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
118
118
|
className: "flex shrink-0 flex-col items-center gap-3",
|
|
119
119
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -199,7 +199,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
199
199
|
})]
|
|
200
200
|
})]
|
|
201
201
|
}), /* @__PURE__ */ jsx("ul", {
|
|
202
|
-
className: "flex w-
|
|
202
|
+
className: "flex min-w-[200px] max-w-[300px] flex-col gap-1",
|
|
203
203
|
children: legendRows.map((r) => {
|
|
204
204
|
const isActive = hoverKey === r.key;
|
|
205
205
|
const dimmed = hoverKey != null && !isActive;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics-chart-pie.js","names":[],"sources":["../../../../src/components/analytics-chart/analytics-chart-pie.tsx"],"sourcesContent":["import { MagnifyingGlassMinusIcon } from \"@phosphor-icons/react\";\nimport { cn } from \"@stackframe/stack-ui\";\nimport { type DesignChartConfig, DesignChartContainer } from \"../chart-container\";\nimport { type CSSProperties, type Ref, useMemo } from \"react\";\nimport { Cell, Pie, PieChart } from \"recharts\";\nimport { TrendPill } from \"./default-analytics-chart-tooltip\";\nimport { formatDelta } from \"./format\";\nimport type { AnalyticsChartStrings } from \"./strings\";\nimport {\n cssIdent,\n type AnalyticsChartPieProps,\n type AnalyticsChartSeries,\n type FormatKind,\n type Point,\n} from \"./types\";\n\ntype SegmentColors = {\n primary: { light: string[], dark: string[] },\n compare: { light: string[], dark: string[] },\n};\n\ntype AnalyticsChartPieBodyProps = {\n wrapperRef: Ref<HTMLDivElement>,\n primarySegmentSeries: readonly AnalyticsChartSeries[],\n compareSegmentSeries: readonly AnalyticsChartSeries[],\n aggregatedPrimarySegments: number[],\n aggregatedCompareSegments: number[],\n aggregatedPrimaryTotal: number,\n aggregatedCompareTotal: number,\n segmentColors: SegmentColors,\n showPrimary: boolean,\n showCompare: boolean,\n xFormatKind: FormatKind,\n yFormatKind: FormatKind,\n hoverKey: string | null,\n setHoverKey: (k: string | null) => void,\n zoomRange: [number, number] | null,\n onResetZoom: () => void,\n visibleStart: number,\n visibleEnd: number,\n fullData: Point[],\n strings: AnalyticsChartStrings,\n fmtValue: (value: number, kind: FormatKind) => string,\n pie: AnalyticsChartPieProps | undefined,\n};\n\nconst DEFAULT_PIE_CLASSNAME = \"aspect-square h-[220px] w-[220px] sm:h-[240px] sm:w-[240px]\";\n\nexport function AnalyticsChartPie({\n wrapperRef,\n primarySegmentSeries,\n compareSegmentSeries,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n aggregatedPrimaryTotal,\n aggregatedCompareTotal,\n segmentColors,\n showPrimary,\n showCompare,\n xFormatKind,\n yFormatKind,\n hoverKey,\n setHoverKey,\n zoomRange,\n onResetZoom,\n visibleStart,\n visibleEnd,\n fullData,\n strings,\n fmtValue,\n pie,\n}: AnalyticsChartPieBodyProps) {\n const innerRadius = pie?.innerRadius ?? 60;\n const outerRadius = pie?.outerRadius ?? 84;\n const compareInnerRadius = pie?.compareInnerRadius ?? 36;\n const compareOuterRadius = pie?.compareOuterRadius ?? 52;\n const containerClassName = pie?.className ?? DEFAULT_PIE_CLASSNAME;\n\n const canonicalSeries = primarySegmentSeries.length > 0\n ? primarySegmentSeries\n : compareSegmentSeries;\n const usePrimaryForCanonical = primarySegmentSeries.length > 0;\n\n const compareValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n compareSegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedCompareSegments[sIdx] ?? 0);\n });\n return m;\n }, [compareSegmentSeries, aggregatedCompareSegments]);\n const primaryValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n primarySegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedPrimarySegments[sIdx] ?? 0);\n });\n return m;\n }, [primarySegmentSeries, aggregatedPrimarySegments]);\n\n const canonicalTotal = usePrimaryForCanonical\n ? aggregatedPrimaryTotal\n : aggregatedCompareTotal;\n\n const legendRows = useMemo(\n () =>\n canonicalSeries\n .map((s, sIdx) => {\n const value = usePrimaryForCanonical\n ? (aggregatedPrimarySegments[sIdx] ?? 0)\n : (aggregatedCompareSegments[sIdx] ?? 0);\n const prevValue = usePrimaryForCanonical\n ? (compareValueByKey.get(s.key) ?? 0)\n : (primaryValueByKey.get(s.key) ?? 0);\n return {\n key: s.key,\n label: s.label,\n sIdx,\n value,\n prevValue,\n pct: canonicalTotal > 0 ? value / canonicalTotal : 0,\n fill: segmentColors.primary.light[sIdx],\n fillDark: segmentColors.primary.dark[sIdx],\n fillCompare: segmentColors.compare.light[sIdx],\n fillCompareDark: segmentColors.compare.dark[sIdx],\n };\n })\n .sort((a, b) => b.value - a.value),\n [\n canonicalSeries,\n usePrimaryForCanonical,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n compareValueByKey,\n primaryValueByKey,\n canonicalTotal,\n segmentColors,\n ],\n );\n\n const chartConfig = useMemo<DesignChartConfig>(() => {\n const config: DesignChartConfig = {};\n canonicalSeries.forEach((s, sIdx) => {\n config[cssIdent(s.key)] = {\n label: s.label,\n theme: {\n light: segmentColors.primary.light[sIdx],\n dark: segmentColors.primary.dark[sIdx],\n },\n };\n config[`compare-${cssIdent(s.key)}`] = {\n label: s.label,\n theme: {\n light: segmentColors.compare.light[sIdx],\n dark: segmentColors.compare.dark[sIdx],\n },\n };\n });\n return config;\n }, [canonicalSeries, segmentColors]);\n\n const activeRow = hoverKey\n ? legendRows.find((r) => r.key === hoverKey) ?? null\n : null;\n const activeDelta = activeRow\n ? formatDelta(activeRow.value, activeRow.prevValue)\n : formatDelta(aggregatedPrimaryTotal, aggregatedCompareTotal);\n\n const windowDays = visibleEnd - visibleStart + 1;\n const startLabel = fmtValue(fullData[visibleStart]!.ts, xFormatKind);\n const endLabel = fmtValue(fullData[visibleEnd]!.ts, xFormatKind);\n\n const outerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.value, fill: r.fill }));\n const innerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.prevValue, fill: r.fillCompare }));\n const activeIdx = hoverKey ? legendRows.findIndex((r) => r.key === hoverKey) : -1;\n\n return (\n <div\n ref={wrapperRef}\n className=\"relative w-full select-none\"\n onClick={(e) => e.stopPropagation()}\n >\n {zoomRange && (\n <div className=\"absolute right-2 top-2 z-20\">\n <button\n type=\"button\"\n onClick={onResetZoom}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-wider text-blue-600 ring-1 ring-blue-500/30 transition-colors duration-150 hover:bg-blue-500/15 hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 dark:text-blue-300 dark:ring-blue-400/30\"\n >\n <MagnifyingGlassMinusIcon weight=\"bold\" className=\"size-3\" aria-hidden=\"true\" />\n <span>{strings.resetZoom}</span>\n </button>\n </div>\n )}\n\n <div className=\"flex min-h-[260px] flex-col items-center gap-6 sm:min-h-[320px] sm:flex-row sm:items-center sm:justify-center sm:gap-10\">\n <div className=\"flex shrink-0 flex-col items-center gap-3\">\n <div className=\"relative\">\n <DesignChartContainer config={chartConfig} className={containerClassName}>\n <PieChart\n role=\"img\"\n aria-label={strings.pieAriaLabel({\n segmentCount: canonicalSeries.length,\n windowDays,\n })}\n >\n {showPrimary && (\n <Pie\n data={outerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {outerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`outer-${d.name}`}\n fill={`var(--color-${d.name})`}\n opacity={inactive ? 0.22 : 1}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n {showCompare && (\n <Pie\n data={innerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={compareInnerRadius}\n outerRadius={compareOuterRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {innerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`inner-${d.name}`}\n fill={`var(--color-compare-${d.name})`}\n opacity={inactive ? 0.22 : 0.95}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n </PieChart>\n </DesignChartContainer>\n\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center\">\n <span className=\"block max-w-[68px] truncate font-mono text-[9px] uppercase tracking-wider text-muted-foreground\">\n {activeRow ? activeRow.label : strings.pieTotalCenter}\n </span>\n <span className=\"mt-0.5 block max-w-[72px] truncate font-mono text-xl font-semibold leading-none tabular-nums text-foreground\">\n {fmtValue(activeRow ? activeRow.value : canonicalTotal, yFormatKind)}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 text-center\">\n <span className=\"font-mono text-[10px] uppercase tracking-wider text-muted-foreground/80\">\n {startLabel} – {endLabel}\n </span>\n {showCompare && (\n <TrendPill delta={activeDelta} size=\"sm\" label={strings.pieVsPrev} />\n )}\n </div>\n </div>\n\n <ul className=\"flex w-full max-w-[380px] flex-col gap-1\">\n {legendRows.map((r) => {\n const isActive = hoverKey === r.key;\n const dimmed = hoverKey != null && !isActive;\n const rowDelta = formatDelta(r.value, r.prevValue);\n return (\n <li key={r.key}>\n <button\n type=\"button\"\n onMouseEnter={() => setHoverKey(r.key)}\n onMouseLeave={() => setHoverKey(null)}\n onFocus={() => setHoverKey(r.key)}\n onBlur={() => setHoverKey(null)}\n className={cn(\n \"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left transition-[background-color,opacity] duration-150 hover:bg-foreground/[0.04] hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20\",\n isActive && \"bg-foreground/[0.05]\",\n dimmed && \"opacity-50\",\n )}\n >\n <span\n className=\"size-2.5 shrink-0 rounded-[3px] bg-[var(--c-l)] dark:bg-[var(--c-d)]\"\n style={{ \"--c-l\": r.fill, \"--c-d\": r.fillDark } as CSSProperties}\n />\n <span className=\"min-w-0 flex-1 truncate text-[12px] font-medium text-foreground\">\n {r.label}\n </span>\n <span className=\"shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground\">\n {(r.pct * 100).toFixed(1)}%\n </span>\n <span className=\"shrink-0 min-w-[48px] text-right font-mono text-[11px] font-semibold tabular-nums text-foreground\">\n {fmtValue(r.value, yFormatKind)}\n </span>\n {showCompare && (\n <span className=\"shrink-0\">\n <TrendPill delta={rowDelta} size=\"sm\" />\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"analytics-chart-pie.js","names":[],"sources":["../../../../src/components/analytics-chart/analytics-chart-pie.tsx"],"sourcesContent":["import { MagnifyingGlassMinusIcon } from \"@phosphor-icons/react\";\nimport { cn } from \"@stackframe/stack-ui\";\nimport { type DesignChartConfig, DesignChartContainer } from \"../chart-container\";\nimport { type CSSProperties, type Ref, useMemo } from \"react\";\nimport { Cell, Pie, PieChart } from \"recharts\";\nimport { TrendPill } from \"./default-analytics-chart-tooltip\";\nimport { formatDelta } from \"./format\";\nimport type { AnalyticsChartStrings } from \"./strings\";\nimport {\n cssIdent,\n type AnalyticsChartPieProps,\n type AnalyticsChartSeries,\n type FormatKind,\n type Point,\n} from \"./types\";\n\ntype SegmentColors = {\n primary: { light: string[], dark: string[] },\n compare: { light: string[], dark: string[] },\n};\n\ntype AnalyticsChartPieBodyProps = {\n wrapperRef: Ref<HTMLDivElement>,\n primarySegmentSeries: readonly AnalyticsChartSeries[],\n compareSegmentSeries: readonly AnalyticsChartSeries[],\n aggregatedPrimarySegments: number[],\n aggregatedCompareSegments: number[],\n aggregatedPrimaryTotal: number,\n aggregatedCompareTotal: number,\n segmentColors: SegmentColors,\n showPrimary: boolean,\n showCompare: boolean,\n xFormatKind: FormatKind,\n yFormatKind: FormatKind,\n hoverKey: string | null,\n setHoverKey: (k: string | null) => void,\n zoomRange: [number, number] | null,\n onResetZoom: () => void,\n visibleStart: number,\n visibleEnd: number,\n fullData: Point[],\n strings: AnalyticsChartStrings,\n fmtValue: (value: number, kind: FormatKind) => string,\n pie: AnalyticsChartPieProps | undefined,\n};\n\nconst DEFAULT_PIE_CLASSNAME = \"aspect-square h-[220px] w-[220px] sm:h-[240px] sm:w-[240px]\";\n\nexport function AnalyticsChartPie({\n wrapperRef,\n primarySegmentSeries,\n compareSegmentSeries,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n aggregatedPrimaryTotal,\n aggregatedCompareTotal,\n segmentColors,\n showPrimary,\n showCompare,\n xFormatKind,\n yFormatKind,\n hoverKey,\n setHoverKey,\n zoomRange,\n onResetZoom,\n visibleStart,\n visibleEnd,\n fullData,\n strings,\n fmtValue,\n pie,\n}: AnalyticsChartPieBodyProps) {\n const innerRadius = pie?.innerRadius ?? 60;\n const outerRadius = pie?.outerRadius ?? 84;\n const compareInnerRadius = pie?.compareInnerRadius ?? 36;\n const compareOuterRadius = pie?.compareOuterRadius ?? 52;\n const containerClassName = pie?.className ?? DEFAULT_PIE_CLASSNAME;\n\n const canonicalSeries = primarySegmentSeries.length > 0\n ? primarySegmentSeries\n : compareSegmentSeries;\n const usePrimaryForCanonical = primarySegmentSeries.length > 0;\n\n const compareValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n compareSegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedCompareSegments[sIdx] ?? 0);\n });\n return m;\n }, [compareSegmentSeries, aggregatedCompareSegments]);\n const primaryValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n primarySegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedPrimarySegments[sIdx] ?? 0);\n });\n return m;\n }, [primarySegmentSeries, aggregatedPrimarySegments]);\n\n const canonicalTotal = usePrimaryForCanonical\n ? aggregatedPrimaryTotal\n : aggregatedCompareTotal;\n\n const legendRows = useMemo(\n () =>\n canonicalSeries\n .map((s, sIdx) => {\n const value = usePrimaryForCanonical\n ? (aggregatedPrimarySegments[sIdx] ?? 0)\n : (aggregatedCompareSegments[sIdx] ?? 0);\n const prevValue = usePrimaryForCanonical\n ? (compareValueByKey.get(s.key) ?? 0)\n : (primaryValueByKey.get(s.key) ?? 0);\n return {\n key: s.key,\n label: s.label,\n sIdx,\n value,\n prevValue,\n pct: canonicalTotal > 0 ? value / canonicalTotal : 0,\n fill: segmentColors.primary.light[sIdx],\n fillDark: segmentColors.primary.dark[sIdx],\n fillCompare: segmentColors.compare.light[sIdx],\n fillCompareDark: segmentColors.compare.dark[sIdx],\n };\n })\n .sort((a, b) => b.value - a.value),\n [\n canonicalSeries,\n usePrimaryForCanonical,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n compareValueByKey,\n primaryValueByKey,\n canonicalTotal,\n segmentColors,\n ],\n );\n\n const chartConfig = useMemo<DesignChartConfig>(() => {\n const config: DesignChartConfig = {};\n canonicalSeries.forEach((s, sIdx) => {\n config[cssIdent(s.key)] = {\n label: s.label,\n theme: {\n light: segmentColors.primary.light[sIdx],\n dark: segmentColors.primary.dark[sIdx],\n },\n };\n config[`compare-${cssIdent(s.key)}`] = {\n label: s.label,\n theme: {\n light: segmentColors.compare.light[sIdx],\n dark: segmentColors.compare.dark[sIdx],\n },\n };\n });\n return config;\n }, [canonicalSeries, segmentColors]);\n\n const activeRow = hoverKey\n ? legendRows.find((r) => r.key === hoverKey) ?? null\n : null;\n const activeDelta = activeRow\n ? formatDelta(activeRow.value, activeRow.prevValue)\n : formatDelta(aggregatedPrimaryTotal, aggregatedCompareTotal);\n\n const windowDays = visibleEnd - visibleStart + 1;\n const startLabel = fmtValue(fullData[visibleStart]!.ts, xFormatKind);\n const endLabel = fmtValue(fullData[visibleEnd]!.ts, xFormatKind);\n\n const outerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.value, fill: r.fill }));\n const innerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.prevValue, fill: r.fillCompare }));\n const activeIdx = hoverKey ? legendRows.findIndex((r) => r.key === hoverKey) : -1;\n\n return (\n <div\n ref={wrapperRef}\n className=\"relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col select-none\"\n onClick={(e) => e.stopPropagation()}\n >\n {zoomRange && (\n <div className=\"absolute right-2 top-2 z-20\">\n <button\n type=\"button\"\n onClick={onResetZoom}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-wider text-blue-600 ring-1 ring-blue-500/30 transition-colors duration-150 hover:bg-blue-500/15 hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 dark:text-blue-300 dark:ring-blue-400/30\"\n >\n <MagnifyingGlassMinusIcon weight=\"bold\" className=\"size-3\" aria-hidden=\"true\" />\n <span>{strings.resetZoom}</span>\n </button>\n </div>\n )}\n\n <div className=\"flex h-full min-h-0 w-full flex-1 flex-wrap content-center items-center justify-center gap-x-10 gap-y-6\">\n <div className=\"flex shrink-0 flex-col items-center gap-3\">\n <div className=\"relative\">\n <DesignChartContainer config={chartConfig} className={containerClassName}>\n <PieChart\n role=\"img\"\n aria-label={strings.pieAriaLabel({\n segmentCount: canonicalSeries.length,\n windowDays,\n })}\n >\n {showPrimary && (\n <Pie\n data={outerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {outerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`outer-${d.name}`}\n fill={`var(--color-${d.name})`}\n opacity={inactive ? 0.22 : 1}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n {showCompare && (\n <Pie\n data={innerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={compareInnerRadius}\n outerRadius={compareOuterRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {innerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`inner-${d.name}`}\n fill={`var(--color-compare-${d.name})`}\n opacity={inactive ? 0.22 : 0.95}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n </PieChart>\n </DesignChartContainer>\n\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center\">\n <span className=\"block max-w-[68px] truncate font-mono text-[9px] uppercase tracking-wider text-muted-foreground\">\n {activeRow ? activeRow.label : strings.pieTotalCenter}\n </span>\n <span className=\"mt-0.5 block max-w-[72px] truncate font-mono text-xl font-semibold leading-none tabular-nums text-foreground\">\n {fmtValue(activeRow ? activeRow.value : canonicalTotal, yFormatKind)}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 text-center\">\n <span className=\"font-mono text-[10px] uppercase tracking-wider text-muted-foreground/80\">\n {startLabel} – {endLabel}\n </span>\n {showCompare && (\n <TrendPill delta={activeDelta} size=\"sm\" label={strings.pieVsPrev} />\n )}\n </div>\n </div>\n\n <ul className=\"flex min-w-[200px] max-w-[300px] flex-col gap-1\">\n {legendRows.map((r) => {\n const isActive = hoverKey === r.key;\n const dimmed = hoverKey != null && !isActive;\n const rowDelta = formatDelta(r.value, r.prevValue);\n return (\n <li key={r.key}>\n <button\n type=\"button\"\n onMouseEnter={() => setHoverKey(r.key)}\n onMouseLeave={() => setHoverKey(null)}\n onFocus={() => setHoverKey(r.key)}\n onBlur={() => setHoverKey(null)}\n className={cn(\n \"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left transition-[background-color,opacity] duration-150 hover:bg-foreground/[0.04] hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20\",\n isActive && \"bg-foreground/[0.05]\",\n dimmed && \"opacity-50\",\n )}\n >\n <span\n className=\"size-2.5 shrink-0 rounded-[3px] bg-[var(--c-l)] dark:bg-[var(--c-d)]\"\n style={{ \"--c-l\": r.fill, \"--c-d\": r.fillDark } as CSSProperties}\n />\n <span className=\"min-w-0 flex-1 truncate text-[12px] font-medium text-foreground\">\n {r.label}\n </span>\n <span className=\"shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground\">\n {(r.pct * 100).toFixed(1)}%\n </span>\n <span className=\"shrink-0 min-w-[48px] text-right font-mono text-[11px] font-semibold tabular-nums text-foreground\">\n {fmtValue(r.value, yFormatKind)}\n </span>\n {showCompare && (\n <span className=\"shrink-0\">\n <TrendPill delta={rowDelta} size=\"sm\" />\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -2,10 +2,11 @@ import { CSSProperties } from "react";
|
|
|
2
2
|
import { DataGridColumnDef } from "./types";
|
|
3
3
|
|
|
4
4
|
//#region src/components/data-grid/data-grid-sizing.d.ts
|
|
5
|
+
declare function getEffectiveMinWidth<TRow>(col: DataGridColumnDef<TRow>): number;
|
|
5
6
|
declare function getColumnSizingStyle<TRow>(col: DataGridColumnDef<TRow>): CSSProperties;
|
|
6
7
|
declare function createGridSizingStyle(widths: ReadonlyMap<string, number>, totalWidth: number): Record<string, string>;
|
|
7
8
|
declare function applyDraggedColumnWidth(el: HTMLElement, columnId: string, width: number, totalWidth: number): void;
|
|
8
9
|
declare function clampColumnWidth<TRow>(col: DataGridColumnDef<TRow>, width: number): number;
|
|
9
10
|
//#endregion
|
|
10
|
-
export { applyDraggedColumnWidth, clampColumnWidth, createGridSizingStyle, getColumnSizingStyle };
|
|
11
|
+
export { applyDraggedColumnWidth, clampColumnWidth, createGridSizingStyle, getColumnSizingStyle, getEffectiveMinWidth };
|
|
11
12
|
//# sourceMappingURL=data-grid-sizing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-sizing.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid-sizing.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"data-grid-sizing.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid-sizing.ts"],"mappings":";;;;iBAgEgB,oBAAA,MAAA,CAA2B,GAAA,EAAK,iBAAA,CAAkB,IAAA;AAAA,iBAWlD,oBAAA,MAAA,CAA2B,GAAA,EAAK,iBAAA,CAAkB,IAAA,IAAQ,aAAA;AAAA,iBAW1D,qBAAA,CACd,MAAA,EAAQ,WAAA,kBACR,UAAA,WACC,MAAA;AAAA,iBAQa,uBAAA,CACd,EAAA,EAAI,WAAA,EACJ,QAAA,UACA,KAAA,UACA,UAAA;AAAA,iBAMc,gBAAA,MAAA,CAAuB,GAAA,EAAK,iBAAA,CAAkB,IAAA,GAAO,KAAA"}
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
//#region src/components/data-grid/data-grid-sizing.ts
|
|
2
2
|
function colVar(id) {
|
|
3
|
+
if (process.env.NODE_ENV !== "production" && !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(id)) console.warn(`[DataGrid] column id ${JSON.stringify(id)} contains characters that are not safe in a CSS custom property name. Prefer ascii identifiers.`);
|
|
3
4
|
return `--col-${id}`;
|
|
4
5
|
}
|
|
6
|
+
const MIN_COL_WIDTH = 20;
|
|
7
|
+
const MIN_CUSTOM_HEADER_WIDTH = 50;
|
|
8
|
+
const DEFAULT_MAX_COL_WIDTH = 800;
|
|
9
|
+
const HEADER_CHROME_PX = 44;
|
|
10
|
+
let measureContext = null;
|
|
11
|
+
const headerWidthCache = /* @__PURE__ */ new Map();
|
|
12
|
+
function measureHeaderLabelWidth(label) {
|
|
13
|
+
const cached = headerWidthCache.get(label);
|
|
14
|
+
if (cached != null) return cached;
|
|
15
|
+
if (typeof document === "undefined") return 0;
|
|
16
|
+
if (measureContext == null) measureContext = document.createElement("canvas").getContext("2d");
|
|
17
|
+
if (measureContext == null) return 0;
|
|
18
|
+
measureContext.font = "600 12px system-ui, -apple-system, sans-serif";
|
|
19
|
+
const text = label.toUpperCase();
|
|
20
|
+
const width = Math.ceil(measureContext.measureText(text).width + .05 * 12 * text.length);
|
|
21
|
+
headerWidthCache.set(label, width);
|
|
22
|
+
return width;
|
|
23
|
+
}
|
|
24
|
+
function getEffectiveMinWidth(col) {
|
|
25
|
+
if (col.minWidth != null) return col.minWidth;
|
|
26
|
+
const label = typeof col.header === "string" ? col.header : null;
|
|
27
|
+
if (label == null) return typeof col.header === "function" ? MIN_CUSTOM_HEADER_WIDTH : MIN_COL_WIDTH;
|
|
28
|
+
return Math.max(MIN_COL_WIDTH, measureHeaderLabelWidth(label) + HEADER_CHROME_PX);
|
|
29
|
+
}
|
|
5
30
|
function getColumnSizingStyle(col) {
|
|
6
31
|
const w = `var(${colVar(col.id)})`;
|
|
32
|
+
const grow = col.flex ?? 0;
|
|
7
33
|
return {
|
|
8
|
-
flex:
|
|
34
|
+
flex: `${grow} 0 ${w}`,
|
|
9
35
|
width: w,
|
|
10
|
-
minWidth: col
|
|
11
|
-
maxWidth: col.maxWidth ??
|
|
36
|
+
minWidth: getEffectiveMinWidth(col),
|
|
37
|
+
maxWidth: grow > 0 ? void 0 : col.maxWidth ?? DEFAULT_MAX_COL_WIDTH
|
|
12
38
|
};
|
|
13
39
|
}
|
|
14
40
|
function createGridSizingStyle(widths, totalWidth) {
|
|
@@ -21,9 +47,11 @@ function applyDraggedColumnWidth(el, columnId, width, totalWidth) {
|
|
|
21
47
|
el.style.setProperty("--grid-total-w", `${totalWidth}px`);
|
|
22
48
|
}
|
|
23
49
|
function clampColumnWidth(col, width) {
|
|
24
|
-
|
|
50
|
+
const minWidth = getEffectiveMinWidth(col);
|
|
51
|
+
const maxWidth = col.maxWidth ?? DEFAULT_MAX_COL_WIDTH;
|
|
52
|
+
return Math.max(minWidth, Math.min(maxWidth, width));
|
|
25
53
|
}
|
|
26
54
|
|
|
27
55
|
//#endregion
|
|
28
|
-
export { applyDraggedColumnWidth, clampColumnWidth, createGridSizingStyle, getColumnSizingStyle };
|
|
56
|
+
export { applyDraggedColumnWidth, clampColumnWidth, createGridSizingStyle, getColumnSizingStyle, getEffectiveMinWidth };
|
|
29
57
|
//# sourceMappingURL=data-grid-sizing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-sizing.js","names":[],"sources":["../../../../src/components/data-grid/data-grid-sizing.ts"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { DataGridColumnDef } from \"./types\";\n\n// CSS variable names for column widths set on the grid container.\n// Cells read these via var(...) so a single setProperty during drag\n// resizes every cell in a column with zero React re-renders.\n\nfunction colVar(id: string): `--col-${string}` {\n return `--col-${id}`;\n}\n\nexport function getColumnSizingStyle<TRow>(col: DataGridColumnDef<TRow>): CSSProperties {\n const w = `var(${colVar(col.id)})`;\n return {
|
|
1
|
+
{"version":3,"file":"data-grid-sizing.js","names":[],"sources":["../../../../src/components/data-grid/data-grid-sizing.ts"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { DataGridColumnDef } from \"./types\";\n\n// CSS variable names for column widths set on the grid container.\n// Cells read these via var(...) so a single setProperty during drag\n// resizes every cell in a column with zero React re-renders.\n\nfunction colVar(id: string): `--col-${string}` {\n // Column ids flow into CSS custom-property names. Non-ident chars would\n // break the cascade silently, so fail loud in dev. Consumers should stick\n // to stable ascii-ident ids for every column.\n if (process.env.NODE_ENV !== \"production\" && !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(id)) {\n // eslint-disable-next-line no-console\n console.warn(\n `[DataGrid] column id ${JSON.stringify(id)} contains characters that are not safe in a CSS custom property name. Prefer ascii identifiers.`\n );\n }\n return `--col-${id}`;\n}\n\n// When col.minWidth is not set, the effective minimum is derived from\n// the header label text width so the label is never clipped on resize.\n// Uses an offscreen canvas for zero-layout measurement; results are\n// cached per unique label string.\n\nconst MIN_COL_WIDTH = 20;\nconst MIN_CUSTOM_HEADER_WIDTH = 50;\n// Default upper bound on column width (both initial sizing + resize clamp).\n// Kept generous because consumers can override per-column via `col.maxWidth`.\nconst DEFAULT_MAX_COL_WIDTH = 800;\n// px-3 both sides + gap-1.5 + sort icon (h-3 w-3) + 2px rounding buffer\nconst HEADER_CHROME_PX = 12 + 12 + 6 + 12 + 2;\n\nlet measureContext: CanvasRenderingContext2D | null = null;\nconst headerWidthCache = new Map<string, number>();\n\nfunction measureHeaderLabelWidth(label: string): number {\n const cached = headerWidthCache.get(label);\n if (cached != null) {\n return cached;\n }\n\n if (typeof document === \"undefined\") {\n return 0;\n }\n if (measureContext == null) {\n measureContext = document.createElement(\"canvas\").getContext(\"2d\");\n }\n if (measureContext == null) {\n return 0;\n }\n\n // Match header cell: text-xs (12px) font-semibold (600) uppercase tracking-wider (0.05em)\n measureContext.font = \"600 12px system-ui, -apple-system, sans-serif\";\n const text = label.toUpperCase();\n const letterSpacingPx = 0.05 * 12;\n const width = Math.ceil(\n measureContext.measureText(text).width + letterSpacingPx * text.length,\n );\n\n headerWidthCache.set(label, width);\n return width;\n}\n\nexport function getEffectiveMinWidth<TRow>(col: DataGridColumnDef<TRow>): number {\n if (col.minWidth != null) {\n return col.minWidth;\n }\n const label = typeof col.header === \"string\" ? col.header : null;\n if (label == null) {\n return typeof col.header === \"function\" ? MIN_CUSTOM_HEADER_WIDTH : MIN_COL_WIDTH;\n }\n return Math.max(MIN_COL_WIDTH, measureHeaderLabelWidth(label) + HEADER_CHROME_PX);\n}\n\nexport function getColumnSizingStyle<TRow>(col: DataGridColumnDef<TRow>): CSSProperties {\n const w = `var(${colVar(col.id)})`;\n const grow = col.flex ?? 0;\n return {\n flex: `${grow} 0 ${w}`,\n width: w,\n minWidth: getEffectiveMinWidth(col),\n maxWidth: grow > 0 ? undefined : (col.maxWidth ?? DEFAULT_MAX_COL_WIDTH),\n };\n}\n\nexport function createGridSizingStyle(\n widths: ReadonlyMap<string, number>,\n totalWidth: number,\n): Record<string, string> {\n const style: Record<string, string> = { \"--grid-total-w\": `${totalWidth}px` };\n for (const [id, w] of widths) {\n style[colVar(id)] = `${w}px`;\n }\n return style;\n}\n\nexport function applyDraggedColumnWidth(\n el: HTMLElement,\n columnId: string,\n width: number,\n totalWidth: number,\n) {\n el.style.setProperty(colVar(columnId), `${width}px`);\n el.style.setProperty(\"--grid-total-w\", `${totalWidth}px`);\n}\n\nexport function clampColumnWidth<TRow>(col: DataGridColumnDef<TRow>, width: number): number {\n const minWidth = getEffectiveMinWidth(col);\n const maxWidth = col.maxWidth ?? DEFAULT_MAX_COL_WIDTH;\n return Math.max(minWidth, Math.min(maxWidth, width));\n}\n"],"mappings":";AAOA,SAAS,OAAO,IAA+B;AAI7C,KAAI,QAAQ,IAAI,aAAa,gBAAgB,CAAC,4BAA4B,KAAK,GAAG,CAEhF,SAAQ,KACN,wBAAwB,KAAK,UAAU,GAAG,CAAC,iGAC5C;AAEH,QAAO,SAAS;;AAQlB,MAAM,gBAAgB;AACtB,MAAM,0BAA0B;AAGhC,MAAM,wBAAwB;AAE9B,MAAM,mBAAmB;AAEzB,IAAI,iBAAkD;AACtD,MAAM,mCAAmB,IAAI,KAAqB;AAElD,SAAS,wBAAwB,OAAuB;CACtD,MAAM,SAAS,iBAAiB,IAAI,MAAM;AAC1C,KAAI,UAAU,KACZ,QAAO;AAGT,KAAI,OAAO,aAAa,YACtB,QAAO;AAET,KAAI,kBAAkB,KACpB,kBAAiB,SAAS,cAAc,SAAS,CAAC,WAAW,KAAK;AAEpE,KAAI,kBAAkB,KACpB,QAAO;AAIT,gBAAe,OAAO;CACtB,MAAM,OAAO,MAAM,aAAa;CAEhC,MAAM,QAAQ,KAAK,KACjB,eAAe,YAAY,KAAK,CAAC,QAFX,MAAO,KAE8B,KAAK,OACjE;AAED,kBAAiB,IAAI,OAAO,MAAM;AAClC,QAAO;;AAGT,SAAgB,qBAA2B,KAAsC;AAC/E,KAAI,IAAI,YAAY,KAClB,QAAO,IAAI;CAEb,MAAM,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAC5D,KAAI,SAAS,KACX,QAAO,OAAO,IAAI,WAAW,aAAa,0BAA0B;AAEtE,QAAO,KAAK,IAAI,eAAe,wBAAwB,MAAM,GAAG,iBAAiB;;AAGnF,SAAgB,qBAA2B,KAA6C;CACtF,MAAM,IAAI,OAAO,OAAO,IAAI,GAAG,CAAC;CAChC,MAAM,OAAO,IAAI,QAAQ;AACzB,QAAO;EACL,MAAM,GAAG,KAAK,KAAK;EACnB,OAAO;EACP,UAAU,qBAAqB,IAAI;EACnC,UAAU,OAAO,IAAI,SAAa,IAAI,YAAY;EACnD;;AAGH,SAAgB,sBACd,QACA,YACwB;CACxB,MAAM,QAAgC,EAAE,kBAAkB,GAAG,WAAW,KAAK;AAC7E,MAAK,MAAM,CAAC,IAAI,MAAM,OACpB,OAAM,OAAO,GAAG,IAAI,GAAG,EAAE;AAE3B,QAAO;;AAGT,SAAgB,wBACd,IACA,UACA,OACA,YACA;AACA,IAAG,MAAM,YAAY,OAAO,SAAS,EAAE,GAAG,MAAM,IAAI;AACpD,IAAG,MAAM,YAAY,kBAAkB,GAAG,WAAW,IAAI;;AAG3D,SAAgB,iBAAuB,KAA8B,OAAuB;CAC1F,MAAM,WAAW,qBAAqB,IAAI;CAC1C,MAAM,WAAW,IAAI,YAAY;AACjC,QAAO,KAAK,IAAI,UAAU,KAAK,IAAI,UAAU,MAAM,CAAC"}
|
|
@@ -177,17 +177,21 @@ function DataGridToolbar({ ctx, extra, extraLeading, hideQuickSearch }) {
|
|
|
177
177
|
}, [onChange]);
|
|
178
178
|
const hasDateColumns = useMemo(() => columns.some((c) => c.type === "date" || c.type === "dateTime"), [columns]);
|
|
179
179
|
return /* @__PURE__ */ jsxs("div", {
|
|
180
|
-
className: "flex
|
|
181
|
-
children: [
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
180
|
+
className: "flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2",
|
|
181
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
182
|
+
className: "flex min-w-0 flex-1 flex-wrap items-center gap-2",
|
|
183
|
+
children: [
|
|
184
|
+
!hideQuickSearch && /* @__PURE__ */ jsx(QuickSearch, {
|
|
185
|
+
value: state.quickSearch,
|
|
186
|
+
onChange: updateQuickSearch,
|
|
187
|
+
placeholder: strings.searchPlaceholder
|
|
188
|
+
}),
|
|
189
|
+
extraLeading,
|
|
190
|
+
extra
|
|
191
|
+
]
|
|
192
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
193
|
+
className: "flex shrink-0 items-center justify-end gap-2",
|
|
194
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
191
195
|
className: "relative shrink-0",
|
|
192
196
|
ref: columnPopover.ref,
|
|
193
197
|
children: [/* @__PURE__ */ jsx(ToolbarButton, {
|
|
@@ -208,13 +212,12 @@ function DataGridToolbar({ ctx, extra, extraLeading, hideQuickSearch }) {
|
|
|
208
212
|
hasDateColumns
|
|
209
213
|
})
|
|
210
214
|
})]
|
|
211
|
-
}),
|
|
212
|
-
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
215
|
+
}), /* @__PURE__ */ jsx(ToolbarButton, {
|
|
213
216
|
onClick: exportCsv,
|
|
214
217
|
title: strings.export,
|
|
215
218
|
children: /* @__PURE__ */ jsx(DownloadSimple, { className: "h-3.5 w-3.5" })
|
|
216
|
-
})
|
|
217
|
-
]
|
|
219
|
+
})]
|
|
220
|
+
})]
|
|
218
221
|
});
|
|
219
222
|
}
|
|
220
223
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-toolbar.js","names":[],"sources":["../../../../src/components/data-grid/data-grid-toolbar.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@stackframe/stack-ui\";\nimport {\n Check,\n DownloadSimple,\n Eye,\n EyeSlash,\n MagnifyingGlass,\n X,\n} from \"@phosphor-icons/react\";\nimport React, { useCallback, useMemo, useRef, useState } from \"react\";\nimport type {\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridStrings,\n DataGridToolbarContext,\n} from \"./types\";\n\n// ─── Popover primitive ───────────────────────────────────────────────\n\nfunction usePopover() {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, [open]);\n\n return { open, setOpen, ref };\n}\n\nfunction PopoverPanel({\n children,\n className,\n popoverRef,\n}: {\n children: React.ReactNode;\n className?: string;\n popoverRef: React.Ref<HTMLDivElement>;\n}) {\n return (\n <div\n ref={popoverRef}\n className={cn(\n \"absolute top-full left-0 mt-1 z-50\",\n \"bg-popover text-popover-foreground rounded-xl shadow-lg\",\n \"ring-1 ring-black/[0.08] dark:ring-white/[0.1]\",\n \"backdrop-blur-xl\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\n// ─── Quick search ────────────────────────────────────────────────────\n\nfunction QuickSearch({\n value,\n onChange,\n placeholder,\n}: {\n value: string;\n onChange: (value: string) => void;\n placeholder: string;\n}) {\n return (\n <div className=\"relative flex min-w-0 flex-1 items-center sm:flex-initial\">\n <MagnifyingGlass className=\"absolute left-2.5 h-3.5 w-3.5 text-muted-foreground/50 pointer-events-none\" />\n <input\n type=\"text\"\n className={cn(\n \"h-8 w-full sm:w-52 pl-8 pr-7 rounded-xl text-xs\",\n \"bg-background\",\n \"border border-black/[0.08] dark:border-white/[0.08]\",\n \"placeholder:text-muted-foreground/40\",\n \"focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]\",\n \"transition-all duration-150\",\n )}\n placeholder={placeholder}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n />\n {value && (\n <button\n className=\"absolute right-2 text-muted-foreground/40 hover:text-muted-foreground\"\n onClick={() => onChange(\"\")}\n aria-label=\"Clear search\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n );\n}\n\n// ─── Toolbar button ──────────────────────────────────────────────────\n\nfunction ToolbarButton({\n children,\n onClick,\n active,\n title,\n className: extraClassName,\n}: {\n children: React.ReactNode;\n onClick?: () => void;\n active?: boolean;\n title?: string;\n className?: string;\n}) {\n return (\n <button\n className={cn(\n \"relative flex items-center justify-center rounded-lg text-xs font-medium\",\n \"h-7 w-7\",\n \"transition-colors duration-75\",\n active\n ? \"bg-foreground/[0.06] text-foreground\"\n : \"text-muted-foreground hover:text-foreground hover:bg-foreground/[0.04]\",\n extraClassName,\n )}\n onClick={onClick}\n title={title}\n >\n {children}\n </button>\n );\n}\n\n// ─── Column manager ──────────────────────────────────────────────────\n\nfunction ColumnManager<TRow>({\n columns,\n visibility,\n onChange,\n strings,\n dateDisplay,\n onDateDisplayChange,\n hasDateColumns,\n}: {\n columns: readonly DataGridColumnDef<TRow>[];\n visibility: Record<string, boolean>;\n onChange: (visibility: Record<string, boolean>) => void;\n strings: DataGridStrings;\n dateDisplay: DataGridDateDisplay;\n onDateDisplayChange: (mode: DataGridDateDisplay) => void;\n hasDateColumns: boolean;\n}) {\n const hideableColumns = useMemo(\n () => columns.filter((c) => c.hideable !== false),\n [columns],\n );\n\n const toggleColumn = (id: string) => {\n const current = visibility[id] !== false;\n onChange({ ...visibility, [id]: !current });\n };\n\n const showAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = true;\n onChange(next);\n };\n\n const hideAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = false;\n onChange(next);\n };\n\n return (\n <div className=\"p-2 min-w-[240px] max-w-[300px]\">\n <div className=\"max-h-[280px] overflow-y-auto space-y-0.5\">\n {hideableColumns.map((col) => {\n const visible = visibility[col.id] !== false;\n return (\n <button\n key={col.id}\n className={cn(\n \"flex items-center gap-2 w-full px-2.5 py-1.5 rounded-lg text-xs\",\n \"hover:bg-foreground/[0.06] transition-colors duration-75\",\n visible ? \"text-foreground\" : \"text-muted-foreground/50\",\n )}\n onClick={() => toggleColumn(col.id)}\n >\n {visible ? (\n <Eye className=\"h-3.5 w-3.5 flex-shrink-0 text-blue-500\" />\n ) : (\n <EyeSlash className=\"h-3.5 w-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate text-left\">\n {typeof col.header === \"string\" ? col.header : col.id}\n </span>\n {visible && <Check className=\"h-3 w-3 ml-auto flex-shrink-0 text-blue-500\" />}\n </button>\n );\n })}\n </div>\n <div className=\"flex items-center gap-2 mt-2 pt-2 border-t border-foreground/[0.06]\">\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={showAll}>\n {strings.showAll}\n </button>\n <span className=\"text-muted-foreground/20\">|</span>\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={hideAll}>\n {strings.hideAll}\n </button>\n </div>\n\n {/* Date format toggle — only rendered when at least one column\n uses `type: \"date\"` or `\"dateTime\"`. Toggling writes to\n `state.dateDisplay` and the grid re-renders every date cell. */}\n {hasDateColumns && (\n <div className=\"mt-2 pt-2 border-t border-foreground/[0.06]\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <span className=\"text-[10px] font-medium uppercase tracking-wider text-muted-foreground\">\n {strings.dateFormat}\n </span>\n <div className=\"inline-flex items-center gap-0.5 rounded-lg bg-foreground/[0.04] p-0.5\">\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-75\",\n dateDisplay === \"relative\"\n ? \"bg-background text-foreground shadow-sm ring-1 ring-foreground/[0.06]\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n onClick={() => onDateDisplayChange(\"relative\")}\n >\n {strings.dateFormatRelative}\n </button>\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-75\",\n dateDisplay === \"absolute\"\n ? \"bg-background text-foreground shadow-sm ring-1 ring-foreground/[0.06]\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n onClick={() => onDateDisplayChange(\"absolute\")}\n >\n {strings.dateFormatAbsolute}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Main toolbar ────────────────────────────────────────────────────\n\nexport function DataGridToolbar<TRow>({\n ctx,\n extra,\n extraLeading,\n hideQuickSearch,\n}: {\n ctx: DataGridToolbarContext<TRow>;\n /** Extra content rendered inside the toolbar row, to the left of the\n * built-in columns / export actions. Use this to add table-specific\n * affordances (refresh, custom toggles, row counts) without giving up\n * the default actions. */\n extra?: React.ReactNode;\n /** Extra content rendered at the START of the toolbar row — occupies\n * the same position as the built-in quick search (after it, if the\n * quick search is visible). Use this together with `hideQuickSearch`\n * to fully replace the quick search with a custom input, e.g. an\n * AI-powered search bar. */\n extraLeading?: React.ReactNode;\n /** Whether to hide the built-in quick-search input. When `true`,\n * callers are expected to provide their own search UI via\n * `extraLeading`. */\n hideQuickSearch?: boolean;\n}) {\n const { state, onChange, columns, strings, exportCsv } = ctx;\n\n const columnPopover = usePopover();\n\n const updateVisibility = useCallback(\n (visibility: Record<string, boolean>) => {\n onChange((s) => ({ ...s, columnVisibility: visibility }));\n },\n [onChange],\n );\n\n const updateDateDisplay = useCallback(\n (mode: DataGridDateDisplay) => {\n onChange((s) => ({ ...s, dateDisplay: mode }));\n },\n [onChange],\n );\n\n const updateQuickSearch = useCallback(\n (value: string) => {\n onChange((s) => ({\n ...s,\n quickSearch: value,\n // Reset to first page whenever the search text changes,\n // otherwise you can end up on a page index that no longer\n // exists in the filtered / refetched result set.\n pagination: { ...s.pagination, pageIndex: 0 },\n }));\n },\n [onChange],\n );\n\n const hasDateColumns = useMemo(\n () => columns.some((c) => c.type === \"date\" || c.type === \"dateTime\"),\n [columns],\n );\n\n return (\n <div className=\"flex items-center gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06]\">\n {!hideQuickSearch && (\n <QuickSearch\n value={state.quickSearch}\n onChange={updateQuickSearch}\n placeholder={strings.searchPlaceholder}\n />\n )}\n {extraLeading}\n <div className=\"flex-1 min-w-0\" />\n\n {extra}\n\n <div className=\"relative shrink-0\" ref={columnPopover.ref}>\n <ToolbarButton\n onClick={() => columnPopover.setOpen(!columnPopover.open)}\n active={columnPopover.open}\n title={strings.columns}\n >\n <Eye className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {columnPopover.open && (\n <PopoverPanel popoverRef={columnPopover.ref} className=\"right-0 left-auto\">\n <ColumnManager\n columns={columns}\n visibility={state.columnVisibility}\n onChange={updateVisibility}\n strings={strings}\n dateDisplay={state.dateDisplay}\n onDateDisplayChange={updateDateDisplay}\n hasDateColumns={hasDateColumns}\n />\n </PopoverPanel>\n )}\n </div>\n\n <ToolbarButton onClick={exportCsv} title={strings.export}>\n <DownloadSimple className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-grid-toolbar.js","names":[],"sources":["../../../../src/components/data-grid/data-grid-toolbar.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@stackframe/stack-ui\";\nimport {\n Check,\n DownloadSimple,\n Eye,\n EyeSlash,\n MagnifyingGlass,\n X,\n} from \"@phosphor-icons/react\";\nimport React, { useCallback, useMemo, useRef, useState } from \"react\";\nimport type {\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridStrings,\n DataGridToolbarContext,\n} from \"./types\";\n\n// ─── Popover primitive ───────────────────────────────────────────────\n\nfunction usePopover() {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, [open]);\n\n return { open, setOpen, ref };\n}\n\nfunction PopoverPanel({\n children,\n className,\n popoverRef,\n}: {\n children: React.ReactNode;\n className?: string;\n popoverRef: React.Ref<HTMLDivElement>;\n}) {\n return (\n <div\n ref={popoverRef}\n className={cn(\n \"absolute top-full left-0 mt-1 z-50\",\n \"bg-popover text-popover-foreground rounded-xl shadow-lg\",\n \"ring-1 ring-black/[0.08] dark:ring-white/[0.1]\",\n \"backdrop-blur-xl\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\n// ─── Quick search ────────────────────────────────────────────────────\n\nfunction QuickSearch({\n value,\n onChange,\n placeholder,\n}: {\n value: string;\n onChange: (value: string) => void;\n placeholder: string;\n}) {\n return (\n <div className=\"relative flex min-w-0 flex-1 items-center sm:flex-initial\">\n <MagnifyingGlass className=\"absolute left-2.5 h-3.5 w-3.5 text-muted-foreground/50 pointer-events-none\" />\n <input\n type=\"text\"\n className={cn(\n \"h-8 w-full sm:w-52 pl-8 pr-7 rounded-xl text-xs\",\n \"bg-background\",\n \"border border-black/[0.08] dark:border-white/[0.08]\",\n \"placeholder:text-muted-foreground/40\",\n \"focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]\",\n \"transition-all duration-150\",\n )}\n placeholder={placeholder}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n />\n {value && (\n <button\n className=\"absolute right-2 text-muted-foreground/40 hover:text-muted-foreground\"\n onClick={() => onChange(\"\")}\n aria-label=\"Clear search\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n );\n}\n\n// ─── Toolbar button ──────────────────────────────────────────────────\n\nfunction ToolbarButton({\n children,\n onClick,\n active,\n title,\n className: extraClassName,\n}: {\n children: React.ReactNode;\n onClick?: () => void;\n active?: boolean;\n title?: string;\n className?: string;\n}) {\n return (\n <button\n className={cn(\n \"relative flex items-center justify-center rounded-lg text-xs font-medium\",\n \"h-7 w-7\",\n \"transition-colors duration-75\",\n active\n ? \"bg-foreground/[0.06] text-foreground\"\n : \"text-muted-foreground hover:text-foreground hover:bg-foreground/[0.04]\",\n extraClassName,\n )}\n onClick={onClick}\n title={title}\n >\n {children}\n </button>\n );\n}\n\n// ─── Column manager ──────────────────────────────────────────────────\n\nfunction ColumnManager<TRow>({\n columns,\n visibility,\n onChange,\n strings,\n dateDisplay,\n onDateDisplayChange,\n hasDateColumns,\n}: {\n columns: readonly DataGridColumnDef<TRow>[];\n visibility: Record<string, boolean>;\n onChange: (visibility: Record<string, boolean>) => void;\n strings: DataGridStrings;\n dateDisplay: DataGridDateDisplay;\n onDateDisplayChange: (mode: DataGridDateDisplay) => void;\n hasDateColumns: boolean;\n}) {\n const hideableColumns = useMemo(\n () => columns.filter((c) => c.hideable !== false),\n [columns],\n );\n\n const toggleColumn = (id: string) => {\n const current = visibility[id] !== false;\n onChange({ ...visibility, [id]: !current });\n };\n\n const showAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = true;\n onChange(next);\n };\n\n const hideAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = false;\n onChange(next);\n };\n\n return (\n <div className=\"p-2 min-w-[240px] max-w-[300px]\">\n <div className=\"max-h-[280px] overflow-y-auto space-y-0.5\">\n {hideableColumns.map((col) => {\n const visible = visibility[col.id] !== false;\n return (\n <button\n key={col.id}\n className={cn(\n \"flex items-center gap-2 w-full px-2.5 py-1.5 rounded-lg text-xs\",\n \"hover:bg-foreground/[0.06] transition-colors duration-75\",\n visible ? \"text-foreground\" : \"text-muted-foreground/50\",\n )}\n onClick={() => toggleColumn(col.id)}\n >\n {visible ? (\n <Eye className=\"h-3.5 w-3.5 flex-shrink-0 text-blue-500\" />\n ) : (\n <EyeSlash className=\"h-3.5 w-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate text-left\">\n {typeof col.header === \"string\" ? col.header : col.id}\n </span>\n {visible && <Check className=\"h-3 w-3 ml-auto flex-shrink-0 text-blue-500\" />}\n </button>\n );\n })}\n </div>\n <div className=\"flex items-center gap-2 mt-2 pt-2 border-t border-foreground/[0.06]\">\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={showAll}>\n {strings.showAll}\n </button>\n <span className=\"text-muted-foreground/20\">|</span>\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={hideAll}>\n {strings.hideAll}\n </button>\n </div>\n\n {/* Date format toggle — only rendered when at least one column\n uses `type: \"date\"` or `\"dateTime\"`. Toggling writes to\n `state.dateDisplay` and the grid re-renders every date cell. */}\n {hasDateColumns && (\n <div className=\"mt-2 pt-2 border-t border-foreground/[0.06]\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <span className=\"text-[10px] font-medium uppercase tracking-wider text-muted-foreground\">\n {strings.dateFormat}\n </span>\n <div className=\"inline-flex items-center gap-0.5 rounded-lg bg-foreground/[0.04] p-0.5\">\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-75\",\n dateDisplay === \"relative\"\n ? \"bg-background text-foreground shadow-sm ring-1 ring-foreground/[0.06]\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n onClick={() => onDateDisplayChange(\"relative\")}\n >\n {strings.dateFormatRelative}\n </button>\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-75\",\n dateDisplay === \"absolute\"\n ? \"bg-background text-foreground shadow-sm ring-1 ring-foreground/[0.06]\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n onClick={() => onDateDisplayChange(\"absolute\")}\n >\n {strings.dateFormatAbsolute}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Main toolbar ────────────────────────────────────────────────────\n\nexport function DataGridToolbar<TRow>({\n ctx,\n extra,\n extraLeading,\n hideQuickSearch,\n}: {\n ctx: DataGridToolbarContext<TRow>;\n /** Extra content rendered inside the toolbar row, to the left of the\n * built-in columns / export actions. Use this to add table-specific\n * affordances (refresh, custom toggles, row counts) without giving up\n * the default actions. */\n extra?: React.ReactNode;\n /** Extra content rendered at the START of the toolbar row — occupies\n * the same position as the built-in quick search (after it, if the\n * quick search is visible). Use this together with `hideQuickSearch`\n * to fully replace the quick search with a custom input, e.g. an\n * AI-powered search bar. */\n extraLeading?: React.ReactNode;\n /** Whether to hide the built-in quick-search input. When `true`,\n * callers are expected to provide their own search UI via\n * `extraLeading`. */\n hideQuickSearch?: boolean;\n}) {\n const { state, onChange, columns, strings, exportCsv } = ctx;\n\n const columnPopover = usePopover();\n\n const updateVisibility = useCallback(\n (visibility: Record<string, boolean>) => {\n onChange((s) => ({ ...s, columnVisibility: visibility }));\n },\n [onChange],\n );\n\n const updateDateDisplay = useCallback(\n (mode: DataGridDateDisplay) => {\n onChange((s) => ({ ...s, dateDisplay: mode }));\n },\n [onChange],\n );\n\n const updateQuickSearch = useCallback(\n (value: string) => {\n onChange((s) => ({\n ...s,\n quickSearch: value,\n // Reset to first page whenever the search text changes,\n // otherwise you can end up on a page index that no longer\n // exists in the filtered / refetched result set.\n pagination: { ...s.pagination, pageIndex: 0 },\n }));\n },\n [onChange],\n );\n\n const hasDateColumns = useMemo(\n () => columns.some((c) => c.type === \"date\" || c.type === \"dateTime\"),\n [columns],\n );\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2\">\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2\">\n {!hideQuickSearch && (\n <QuickSearch\n value={state.quickSearch}\n onChange={updateQuickSearch}\n placeholder={strings.searchPlaceholder}\n />\n )}\n {extraLeading}\n {extra}\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-2\">\n <div className=\"relative shrink-0\" ref={columnPopover.ref}>\n <ToolbarButton\n onClick={() => columnPopover.setOpen(!columnPopover.open)}\n active={columnPopover.open}\n title={strings.columns}\n >\n <Eye className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {columnPopover.open && (\n <PopoverPanel popoverRef={columnPopover.ref} className=\"right-0 left-auto\">\n <ColumnManager\n columns={columns}\n visibility={state.columnVisibility}\n onChange={updateVisibility}\n strings={strings}\n dateDisplay={state.dateDisplay}\n onDateDisplayChange={updateDateDisplay}\n hasDateColumns={hasDateColumns}\n />\n </PopoverPanel>\n )}\n </div>\n\n <ToolbarButton onClick={exportCsv} title={strings.export}>\n <DownloadSimple className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
|
2
2
|
import { DataGridProps } from "./types";
|
|
3
3
|
|
|
4
4
|
//#region src/components/data-grid/data-grid.d.ts
|
|
5
|
+
declare function isDataGridInteractiveRowClickTarget(target: EventTarget | null): boolean;
|
|
5
6
|
/**
|
|
6
7
|
* Interactive table with sorting, quick search, pagination, selection,
|
|
7
8
|
* and virtualization. Handles 10k+ rows smoothly. Pair with
|
|
@@ -188,6 +189,39 @@ import { DataGridProps } from "./types";
|
|
|
188
189
|
* and footer all call it for you. You do not need to wire any of this
|
|
189
190
|
* manually.
|
|
190
191
|
*
|
|
192
|
+
* ## Cell overflow and dynamic row heights
|
|
193
|
+
*
|
|
194
|
+
* By default every cell truncates its content with an ellipsis
|
|
195
|
+
* (`cellOverflow: "truncate"`). For columns whose content should wrap
|
|
196
|
+
* — badge lists, multi-line text, permission chips — set
|
|
197
|
+
* `cellOverflow: "wrap"` on the column definition.
|
|
198
|
+
*
|
|
199
|
+
* To let rows grow to fit their tallest cell, set `rowHeight="auto"`
|
|
200
|
+
* on the grid. The virtualizer will measure each row after render and
|
|
201
|
+
* adjust scroll positions accordingly. Pair with `estimatedRowHeight`
|
|
202
|
+
* (default 44) for better scroll-position estimates before measurement.
|
|
203
|
+
*
|
|
204
|
+
* ```tsx
|
|
205
|
+
* // Columns: UUIDs truncate, auth-method badges wrap
|
|
206
|
+
* const columns = [
|
|
207
|
+
* { id: "userId", header: "User ID", width: 130 }, // default truncate
|
|
208
|
+
* { id: "auth", header: "Auth methods", width: 150, cellOverflow: "wrap",
|
|
209
|
+
* renderCell: ({ row }) => (
|
|
210
|
+
* <div className="flex flex-wrap gap-1">
|
|
211
|
+
* {row.authTypes.map((t) => <Badge key={t}>{t}</Badge>)}
|
|
212
|
+
* </div>
|
|
213
|
+
* ),
|
|
214
|
+
* },
|
|
215
|
+
* ];
|
|
216
|
+
*
|
|
217
|
+
* <DataGrid columns={columns} rowHeight="auto" estimatedRowHeight={48} ... />
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* With a fixed numeric `rowHeight` (the default), `cellOverflow: "wrap"`
|
|
221
|
+
* still lets content wrap within the row, but anything exceeding the
|
|
222
|
+
* fixed height is clipped. This is useful when you want controlled
|
|
223
|
+
* wrapping without variable row heights.
|
|
224
|
+
*
|
|
191
225
|
* ## Height and scrolling
|
|
192
226
|
*
|
|
193
227
|
* DataGrid is NOT a card. It has no border, rounded corners, or shadow of
|
|
@@ -229,5 +263,5 @@ import { DataGridProps } from "./types";
|
|
|
229
263
|
*/
|
|
230
264
|
declare function DataGrid<TRow>(props: DataGridProps<TRow>): react_jsx_runtime0.JSX.Element;
|
|
231
265
|
//#endregion
|
|
232
|
-
export { DataGrid };
|
|
266
|
+
export { DataGrid, isDataGridInteractiveRowClickTarget };
|
|
233
267
|
//# sourceMappingURL=data-grid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"data-grid.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid.tsx"],"mappings":";;;;iBAsKgB,mCAAA,CAAoC,MAAA,EAAQ,WAAA;;;AAA5D;;;;;AA+sBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,QAAA,MAAA,CAAe,KAAA,EAAO,aAAA,CAAc,IAAA,IAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|