From 8eaf7c831bfd434bb395aaccc694767c15ac4759 Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Fri, 3 Jul 2026 14:42:28 -0700 Subject: [PATCH 1/9] table view style --- .../elements/card/chart/chart.module.scss | 6 +- .../card/chart/conditional_tabs.module.scss | 14 +- .../elements/card/chart/conditional_tabs.tsx | 2 + .../elements/card/chart/data_chart_line.tsx | 141 ++++++++++++++---- .../card/chart/data_table.module.scss | 11 ++ .../elements/card/chart/data_table.tsx | 2 +- 6 files changed, 141 insertions(+), 35 deletions(-) diff --git a/dataweaver/apps/web/src/components/elements/card/chart/chart.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/chart.module.scss index ce785ddb..ae8015b9 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/chart.module.scss +++ b/dataweaver/apps/web/src/components/elements/card/chart/chart.module.scss @@ -1,12 +1,14 @@ .container { display: flex; - flex-shrink: 0; + flex: 1; flex-direction: column; - padding: 28px; + min-height: 0; + padding: 28px 28px 0; } .header-container { display: flex; + flex-shrink: 0; flex-direction: column; row-gap: 8px; margin-bottom: 16px; diff --git a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss index cb3ff3a7..8bbde4b5 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss +++ b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss @@ -1,6 +1,6 @@ .tabs-container { display: flex; - margin-bottom: 16px; + flex-shrink: 0; border-bottom: 1px solid rgb(var(--color-card-divider)); } @@ -37,3 +37,15 @@ width: 24px; height: 24px; } + +.panel { + &:has(table) { + overflow-y: auto; + + thead { + position: sticky; + top: 0; + z-index: 1; + } + } +} diff --git a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx index 44c5eb21..381e6b36 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx'; import { AnimatePresence, m } from 'motion/react'; import type { ComponentPropsWithRef, ComponentType, ReactNode } from 'react'; import { useId, useState } from 'react'; @@ -59,6 +60,7 @@ export const ConditionalTabs = ({ tabs }: ConditionalTabsProps) => { { + const { points, top = 0 } = props; + const point = points?.[0]; + if (!point) return null; + const x = point.x; + return ( + + ); +}; + +const FullWidthAxisLine = () => { + const plotArea = usePlotArea(); + if (!plotArea) return null; + const y = plotArea.y + plotArea.height; + return ( + + ); +}; -export const DataChartLine = ({ data, width, height }: ChartProps) => { +export const DataChartLine = ({ data }: ChartProps) => { return ( - - - - compactFormatter.format(v)} - /> - - + + + + compactFormatter.format(Number(value))} + /> + + } + formatter={(value) => tooltipFormatter.format(Number(value))} + /> + 15 + ? false + : { + r: 3, + fill: DOT_COLOR, + } + } + activeDot={{ + fill: LINE_COLOR, + stroke: LINE_COLOR, + strokeWidth: 1, + }} + /> + + ); }; diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss index 2ee2f0dc..40cc163a 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss @@ -2,10 +2,21 @@ @include type-label-small; width: 100%; + margin-bottom: 16px; text-align: left; + + tr:nth-child(odd) { + background-color: rgb(var(--color-skeleton-surface)); + } } .table th, .table td { padding: 4px 8px; } + +.table th { + padding-top: 16px; + background-color: rgb(var(--color-card-surface)); + border-bottom: 1px solid rgb(var(--color-card-divider)); +} diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx index 6b935937..6ee1dec0 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx @@ -5,7 +5,7 @@ interface DataTableProps { data: ChartDatum[]; } -const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); +const compactFormatter = new Intl.NumberFormat('en', { notation: 'standard' }); export const DataTable = ({ data }: DataTableProps) => { return ( From 430cc80f2400f10dad2b71a9a2c6280a1e48a75f Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Mon, 6 Jul 2026 12:17:55 -0700 Subject: [PATCH 2/9] fix table scrolling --- .../web/src/components/elements/card/base.tsx | 19 ++++++++++--------- .../card/chart/data_table.module.scss | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/dataweaver/apps/web/src/components/elements/card/base.tsx b/dataweaver/apps/web/src/components/elements/card/base.tsx index 8c085504..54a47f30 100644 --- a/dataweaver/apps/web/src/components/elements/card/base.tsx +++ b/dataweaver/apps/web/src/components/elements/card/base.tsx @@ -10,7 +10,6 @@ import type { TLShapeId } from 'tldraw'; import { Button } from '~/components/elements/button'; import { CARD_VARIANT_MAX } from '~/components/scopes/atlas/config'; import type { CardVariant } from '~/components/scopes/atlas/helpers'; -import { useCachedResizeValues } from '~/hooks/use_cached_resize_values'; import s from './base.module.scss'; import { useCardAutoHeight } from './use_card_auto_height'; import { useCardClearTextSelection } from './use_card_clear_text_selection'; @@ -68,10 +67,6 @@ export const CardBase = ({ const startDragging = useCardDragHandle(id); - const getCachedCanScroll = useCachedResizeValues((element: HTMLElement) => { - return element.scrollHeight > element.clientHeight; - }); - return (
{ - if (getCachedCanScroll(false, event.currentTarget)) { - event.stopPropagation(); + let el = event.target as HTMLElement | null; + while (el && el !== event.currentTarget.parentElement) { + if (el.scrollHeight > el.clientHeight) { + event.stopPropagation(); + return; + } + el = el.parentElement; } }} // Once the card is the single selection, reserve dragging for the diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss index 40cc163a..4773b572 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_table.module.scss @@ -18,5 +18,5 @@ .table th { padding-top: 16px; background-color: rgb(var(--color-card-surface)); - border-bottom: 1px solid rgb(var(--color-card-divider)); + box-shadow: inset 0 -1px 0 rgb(var(--color-card-divider)); } From 14ede7a1d1eecf044acfd14866371950be2a4d96 Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Mon, 6 Jul 2026 14:03:09 -0700 Subject: [PATCH 3/9] fix for chart cards not resizing properly on import --- .../elements/card/use_card_auto_height.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts b/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts index db37140a..15c4945e 100644 --- a/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts +++ b/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts @@ -76,8 +76,23 @@ export const useCardAutoHeight = ( }; sync(); - const observer = new ResizeObserver(sync); - observer.observe(container); - return () => observer.disconnect(); + + // --- ResizeObserver: catches size changes of the container itself --- + const resizeObserver = new ResizeObserver(sync); + resizeObserver.observe(container); + + // --- MutationObserver: catches async child content insertion --- + // Recharts' ResponsiveContainer (and similar libraries) render chart SVGs + // asynchronously after measuring their parent width. Once useCardAutoHeight + // writes a smaller `h`, the container's box is max-height-capped so the + // ResizeObserver won't fire when new children appear (the box doesn't + // grow). A MutationObserver detects these insertions and re-measures. + const mutationObserver = new MutationObserver(sync); + mutationObserver.observe(container, { childList: true, subtree: true }); + + return () => { + resizeObserver.disconnect(); + mutationObserver.disconnect(); + }; }, [editor, shapeId, maxHeight]); }; From 2b9786e0d62b9b883167d0a0f3b0d87736d18579 Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Mon, 6 Jul 2026 14:18:13 -0700 Subject: [PATCH 4/9] add bar chart styles --- .../components/elements/card/chart/chart.tsx | 22 +++--- .../card/chart/data_chart_bar_horizontal.tsx | 67 +++++++++++++++++ .../card/chart/data_chart_bar_vertical.tsx | 71 +++++++++++++++++++ 3 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx create mode 100644 dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx diff --git a/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx b/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx index 03937b6e..797c9cad 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx @@ -19,6 +19,8 @@ import { useQueryActions } from '~/components/scopes/atlas/query_provider'; import type { FacetInfo } from '~/server/types'; import s from './chart.module.scss'; import { ConditionalTabs } from './conditional_tabs'; +import { DataChartBarHorizontal } from './data_chart_bar_horizontal'; +import { DataChartBarVertical } from './data_chart_bar_vertical'; import { DataChartLine } from './data_chart_line'; import { DataTable } from './data_table'; import { FacetSelector } from './facet_selector'; @@ -29,10 +31,6 @@ export interface ChartDatum { value: number; } -// TODO: Get dynamically instead of hard coding here -const CHART_WIDTH = 356; -const CHART_HEIGHT = 200; - export interface CardChartProps extends CardState { id: TLShapeId; title?: string; @@ -60,7 +58,6 @@ export const CardChart = ({ const { open: openExport } = useExportActions(); const { runPrompt } = useQueryActions(); - // TODO: Support the different chart styles (for now we always show bar chart) const [selectedStyle, setSelectedStyle] = useState('bar-vertical'); const [isStyleMenuOpen, setIsStyleMenuOpen] = useState(false); @@ -127,13 +124,14 @@ export const CardChart = ({ { icon: IconLineGraphSingle, label: 'Chart', - children: ( - - ), + children: + selectedStyle === 'bar-vertical' ? ( + + ) : selectedStyle === 'bar-horizontal' ? ( + + ) : ( + + ), }, { icon: IconTable, diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx new file mode 100644 index 00000000..54794cda --- /dev/null +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx @@ -0,0 +1,67 @@ +'use client'; + +import { COLORS } from '@package/tokens/ts'; +import { + Bar, + BarChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; + +import type { ChartDatum } from './chart'; + +const BAR_COLOR = `rgb(${COLORS['card-surface-selected']})`; +const GRID_COLOR = `rgb(${COLORS['card-chart-grid']})`; +const AXIS_COLOR = `rgb(${COLORS['card-content-muted']})`; + +interface ChartProps { + data: ChartDatum[]; +} + +const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); +const tooltipFormatter = new Intl.NumberFormat('en', { + maximumFractionDigits: 2, + notation: 'standard', +}); + +export const DataChartBarHorizontal = ({ data }: ChartProps) => { + return ( + + + + compactFormatter.format(value)} + tickMargin={6} + /> + + tooltipFormatter.format(Number(value))} + /> + + + + ); +}; diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx new file mode 100644 index 00000000..42949150 --- /dev/null +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx @@ -0,0 +1,71 @@ +'use client'; + +import { COLORS } from '@package/tokens/ts'; +import { + Bar, + BarChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; +import type { YAxisTickContentProps } from 'recharts/types/util/types'; + +import type { ChartDatum } from './chart'; + +const BAR_COLOR = `rgb(${COLORS['card-surface-selected']})`; +const GRID_COLOR = `rgb(${COLORS['card-chart-grid']})`; +const AXIS_COLOR = `rgb(${COLORS['card-content-muted']})`; + +interface ChartProps { + data: ChartDatum[]; +} + +const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); +const tooltipFormatter = new Intl.NumberFormat('en', { + maximumFractionDigits: 2, + notation: 'standard', +}); + +const CustomYTick = ({ x, y, payload }: YAxisTickContentProps) => ( + + {compactFormatter.format(payload.value)} + +); + +export const DataChartBarVertical = ({ data }: ChartProps) => { + return ( + + + + + + tooltipFormatter.format(Number(value))} + /> + + + + ); +}; From 2f2af7d48b62920e346cd011dadcdcc583827c3d Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Mon, 6 Jul 2026 15:39:45 -0700 Subject: [PATCH 5/9] fix x & y axis size and padding --- .../card/chart/data_chart_bar_horizontal.tsx | 4 +- .../card/chart/data_chart_bar_vertical.tsx | 77 ++++++++++++++----- .../web/src/components/scopes/atlas/config.ts | 2 +- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx index 54794cda..b33e69e3 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx @@ -31,7 +31,7 @@ export const DataChartBarHorizontal = ({ data }: ChartProps) => { return ( { ( - - {compactFormatter.format(payload.value)} - -); +const CustomCursor = (props: { + x?: number; + y?: number; + width?: number; + height?: number; +}) => { + const { x = 0, width = 0, height = 0 } = props; + return ( + + ); +}; + +const FullWidthAxisLine = () => { + const plotArea = usePlotArea(); + if (!plotArea) return null; + const y = plotArea.y + plotArea.height; + return ( + + ); +}; export const DataChartBarVertical = ({ data }: ChartProps) => { return ( @@ -49,19 +70,35 @@ export const DataChartBarVertical = ({ data }: ChartProps) => { aspect={1.78} style={{ paddingBottom: '28px' }} > - - + + + compactFormatter.format(Number(value))} /> - + } formatter={(value) => tooltipFormatter.format(Number(value))} /> diff --git a/dataweaver/apps/web/src/components/scopes/atlas/config.ts b/dataweaver/apps/web/src/components/scopes/atlas/config.ts index efe4699f..6cf31e56 100644 --- a/dataweaver/apps/web/src/components/scopes/atlas/config.ts +++ b/dataweaver/apps/web/src/components/scopes/atlas/config.ts @@ -95,7 +95,7 @@ export const ZOOM_STEPS: readonly number[] = Array.from( export const CARD_VARIANT_MAX: Record = { text: { w: 420, h: 440 }, table: { w: 650, h: 500 }, - chart: { w: 420, h: 520 }, + chart: { w: 420, h: 720 }, }; /** Minimum gap to keep between a placed card and any other card, in px. */ From ea9fc073a534fbc980f9493ef8892bd9a46cd45c Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Tue, 7 Jul 2026 11:49:22 -0700 Subject: [PATCH 6/9] move tooltip to separate component, update style --- .../card/chart/conditional_tabs.module.scss | 2 ++ .../card/chart/custom_tooltip.module.scss | 19 +++++++++++++ .../elements/card/chart/custom_tooltip.tsx | 27 +++++++++++++++++++ .../card/chart/data_chart_bar_horizontal.tsx | 7 ++--- .../card/chart/data_chart_bar_vertical.tsx | 10 ++----- .../elements/card/chart/data_chart_line.tsx | 10 ++----- 6 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.module.scss create mode 100644 dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.tsx diff --git a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss index 8bbde4b5..2204d28c 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss +++ b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss @@ -39,6 +39,8 @@ } .panel { + font-weight: 500; + &:has(table) { overflow-y: auto; diff --git a/dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.module.scss new file mode 100644 index 00000000..76717800 --- /dev/null +++ b/dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.module.scss @@ -0,0 +1,19 @@ +.tooltip { + @include type-label-large; + + padding: 10px 12px; + color: rgb(var(--color-card-content)); + background-color: rgb(var(--color-card-surface)); + border-radius: 8px; + box-shadow: var(--shadow-elevated); + + p { + margin: 0; + } + + .label { + @include type-label-small; + + margin-bottom: 2px; + } +} diff --git a/dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.tsx b/dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.tsx new file mode 100644 index 00000000..893e5eb8 --- /dev/null +++ b/dataweaver/apps/web/src/components/elements/card/chart/custom_tooltip.tsx @@ -0,0 +1,27 @@ +import styles from './custom_tooltip.module.scss'; + +const tooltipFormatter = new Intl.NumberFormat('en', { + maximumFractionDigits: 2, + notation: 'standard', +}); + +export const CustomTooltip = ({ + active, + payload, + label, +}: { + active?: boolean; + payload?: { value: number }[]; + label?: string; +}) => { + if (active && payload && payload.length) { + const value = tooltipFormatter.format(Number(payload?.[0]?.value)); + return ( +
+

{`${label}`}

+

{`${value}`}

+
+ ); + } + return null; +}; diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx index b33e69e3..2cc92ca9 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_horizontal.tsx @@ -12,6 +12,7 @@ import { } from 'recharts'; import type { ChartDatum } from './chart'; +import { CustomTooltip } from './custom_tooltip'; const BAR_COLOR = `rgb(${COLORS['card-surface-selected']})`; const GRID_COLOR = `rgb(${COLORS['card-chart-grid']})`; @@ -22,10 +23,6 @@ interface ChartProps { } const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); -const tooltipFormatter = new Intl.NumberFormat('en', { - maximumFractionDigits: 2, - notation: 'standard', -}); export const DataChartBarHorizontal = ({ data }: ChartProps) => { return ( @@ -58,7 +55,7 @@ export const DataChartBarHorizontal = ({ data }: ChartProps) => { /> tooltipFormatter.format(Number(value))} + content={} />
diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx index c5cc474c..87db8a7d 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx @@ -13,6 +13,7 @@ import { } from 'recharts'; import type { ChartDatum } from './chart'; +import { CustomTooltip } from './custom_tooltip'; const BAR_COLOR = `rgb(${COLORS['card-surface-selected']})`; const GRID_COLOR = `rgb(${COLORS['card-chart-grid']})`; @@ -23,10 +24,6 @@ interface ChartProps { } const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); -const tooltipFormatter = new Intl.NumberFormat('en', { - maximumFractionDigits: 2, - notation: 'standard', -}); const CustomCursor = (props: { x?: number; @@ -97,10 +94,7 @@ export const DataChartBarVertical = ({ data }: ChartProps) => { tickFormatter={(value) => compactFormatter.format(Number(value))} /> - } - formatter={(value) => tooltipFormatter.format(Number(value))} - /> + } content={} />
diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx index de3b88dc..ae450e14 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx @@ -13,6 +13,7 @@ import { } from 'recharts'; import type { ChartDatum } from './chart'; +import { CustomTooltip } from './custom_tooltip'; const LINE_COLOR = `rgb(${COLORS['card-surface-selected']})`; const GRID_COLOR = `rgb(${COLORS['card-chart-grid']})`; @@ -24,10 +25,6 @@ interface ChartProps { } const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); -const tooltipFormatter = new Intl.NumberFormat('en', { - maximumFractionDigits: 2, - notation: 'standard', -}); const CustomCursor = (props: { points?: { x: number; y: number }[]; @@ -100,10 +97,7 @@ export const DataChartLine = ({ data }: ChartProps) => { tickFormatter={(value) => compactFormatter.format(Number(value))} /> - } - formatter={(value) => tooltipFormatter.format(Number(value))} - /> + } content={} /> Date: Tue, 7 Jul 2026 12:05:24 -0700 Subject: [PATCH 7/9] feedback --- .../apps/web/src/components/elements/card/base.tsx | 9 ++++++--- .../elements/card/chart/conditional_tabs.module.scss | 2 +- .../components/elements/card/chart/conditional_tabs.tsx | 3 +-- .../components/elements/card/chart/custom_tooltip.tsx | 2 +- .../elements/card/chart/data_chart_bar_horizontal.tsx | 4 +++- .../elements/card/chart/data_chart_bar_vertical.tsx | 9 ++++++--- .../components/elements/card/chart/data_chart_line.tsx | 4 +++- .../src/components/elements/card/chart/data_table.tsx | 4 +++- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/dataweaver/apps/web/src/components/elements/card/base.tsx b/dataweaver/apps/web/src/components/elements/card/base.tsx index 54a47f30..cab85370 100644 --- a/dataweaver/apps/web/src/components/elements/card/base.tsx +++ b/dataweaver/apps/web/src/components/elements/card/base.tsx @@ -100,9 +100,12 @@ export const CardBase = ({ onWheelCapture={(event) => { let el = event.target as HTMLElement | null; while (el && el !== event.currentTarget.parentElement) { - if (el.scrollHeight > el.clientHeight) { - event.stopPropagation(); - return; + const style = window.getComputedStyle(el); + if (style.overflowY === 'auto' || style.overflowY === 'scroll') { + if (el.scrollHeight > el.clientHeight) { + event.stopPropagation(); + return; + } } el = el.parentElement; } diff --git a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss index 2204d28c..2aab5957 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss +++ b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.module.scss @@ -47,7 +47,7 @@ thead { position: sticky; top: 0; - z-index: 1; + z-index: $z-index-above-content; } } } diff --git a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx index 381e6b36..792ec2ba 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/conditional_tabs.tsx @@ -1,4 +1,3 @@ -import clsx from 'clsx'; import { AnimatePresence, m } from 'motion/react'; import type { ComponentPropsWithRef, ComponentType, ReactNode } from 'react'; import { useId, useState } from 'react'; @@ -60,7 +59,7 @@ export const ConditionalTabs = ({ tabs }: ConditionalTabsProps) => { { return ( diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx index 87db8a7d..e9cbe827 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_bar_vertical.tsx @@ -23,21 +23,24 @@ interface ChartProps { data: ChartDatum[]; } -const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); +const compactFormatter = new Intl.NumberFormat(undefined, { + notation: 'compact', +}); const CustomCursor = (props: { x?: number; y?: number; width?: number; height?: number; + top?: number; }) => { - const { x = 0, width = 0, height = 0 } = props; + const { x = 0, width = 0, height = 0, top = 0 } = props; return ( diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx index ae450e14..96b186ed 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_chart_line.tsx @@ -24,7 +24,9 @@ interface ChartProps { data: ChartDatum[]; } -const compactFormatter = new Intl.NumberFormat('en', { notation: 'compact' }); +const compactFormatter = new Intl.NumberFormat(undefined, { + notation: 'compact', +}); const CustomCursor = (props: { points?: { x: number; y: number }[]; diff --git a/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx b/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx index 6ee1dec0..fc12ac69 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/data_table.tsx @@ -5,7 +5,9 @@ interface DataTableProps { data: ChartDatum[]; } -const compactFormatter = new Intl.NumberFormat('en', { notation: 'standard' }); +const compactFormatter = new Intl.NumberFormat(undefined, { + notation: 'standard', +}); export const DataTable = ({ data }: DataTableProps) => { return ( From f7150cd6b8ed65573bfe3bbb04875fde2d6de6ae Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Tue, 7 Jul 2026 13:58:32 -0700 Subject: [PATCH 8/9] debounce mutation observer --- .../elements/card/use_card_auto_height.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts b/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts index 15c4945e..32c26175 100644 --- a/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts +++ b/dataweaver/apps/web/src/components/elements/card/use_card_auto_height.ts @@ -87,12 +87,23 @@ export const useCardAutoHeight = ( // writes a smaller `h`, the container's box is max-height-capped so the // ResizeObserver won't fire when new children appear (the box doesn't // grow). A MutationObserver detects these insertions and re-measures. - const mutationObserver = new MutationObserver(sync); + // Debounced via rAF to avoid layout thrashing from rapid subtree mutations + // (e.g. tooltips appearing/disappearing on hover). + let mutationFrameId: number | null = null; + const syncOnMutation = () => { + if (mutationFrameId !== null) return; + mutationFrameId = requestAnimationFrame(() => { + mutationFrameId = null; + sync(); + }); + }; + const mutationObserver = new MutationObserver(syncOnMutation); mutationObserver.observe(container, { childList: true, subtree: true }); return () => { resizeObserver.disconnect(); mutationObserver.disconnect(); + if (mutationFrameId !== null) cancelAnimationFrame(mutationFrameId); }; }, [editor, shapeId, maxHeight]); }; From b7133781e655947d031c715df1e9ca4cb64d1166 Mon Sep 17 00:00:00 2001 From: Scott Agrimson Date: Tue, 7 Jul 2026 17:07:11 -0700 Subject: [PATCH 9/9] fix facet dropdown --- .../web/src/components/elements/card/chart/chart.tsx | 1 - .../elements/card/chart/facet_selector.module.scss | 12 +++++++----- .../web/src/components/scopes/atlas/shapes/card.tsx | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx b/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx index 797c9cad..f360a62c 100644 --- a/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx +++ b/dataweaver/apps/web/src/components/elements/card/chart/chart.tsx @@ -68,7 +68,6 @@ export const CardChart = ({ // Derive chart data from selected facet if facets are available const currentFacet = facets?.find((f) => f.facetId === selectedFacetId); const chartData = currentFacet?.observations ?? data; - return ( { // Each card variant owns its own actions, content and footer; the shape just // hands it the state and content it needs to render itself. #renderCard = (shape: ShapeCard) => { - const { variant, title, description, body, data, relatedQueries } = + const { variant, title, description, body, data, facets, relatedQueries } = shape.props; const isLoading = shape.props.isLoading ?? false; @@ -107,6 +107,7 @@ export class ShapeCardUtil extends ShapeUtil { title={title} description={description} data={data} + facets={facets} relatedQueries={relatedQueries} /> );