@zendir/ui 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/react/core/data/DataTable.js +1 -0
- package/dist/react/core/data/DataTable.js.map +1 -1
- package/dist/react/core/overlays/Tooltip.d.ts +14 -0
- package/dist/react/core/overlays/Tooltip.js +19 -3
- package/dist/react/core/overlays/Tooltip.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @zendir/ui
|
|
2
2
|
|
|
3
|
+
## 0.8.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0a14e84: Fix `DataTable`'s `rowTooltipContent` taking the table apart. Each row was wrapped in the tooltip's own `div`, which between `tbody` and `tr` is not a table row — the rows stopped taking part in the table's layout and their cells reflowed as inline text, several rows to a line, with the columns gone.
|
|
8
|
+
|
|
9
|
+
`Tooltip` gains `asChild`, which attaches the handlers and the ref to the child instead of wrapping it, composing with any handlers the child already has. `DataTable` uses it for rows, so a row keeps `tbody` as its parent whatever is attached to it.
|
|
10
|
+
|
|
3
11
|
## 0.8.1
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../../src/react/core/data/DataTable.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - DataTable Component\r\n *\r\n * Structured data display with expandable groups, responsive columns,\r\n * and theme-aware styling. Designed for telemetry parameter grids,\r\n * link budget displays, spacecraft information panels.\r\n *\r\n * Astro UX Compliance:\r\n * - Tabular monospace numbers for value columns\r\n * - Status-based row coloring\r\n * - Compact mode support\r\n * - Accessible table semantics\r\n *\r\n * Enhanced Tooltip System:\r\n * - Row-level tooltips (string or rich ReactNode via rowTooltipContent)\r\n * - Cell-level tooltips per column definition\r\n * - Header tooltips for column descriptions\r\n * - Built-in DataTableRowDetail component for structured detail views\r\n * - Portal-rendered, WCAG-compliant, animated tooltips\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={[\r\n * { key: 'label', header: 'Parameter', width: '40%', headerTooltip: 'Telemetry parameter name' },\r\n * { key: 'value', header: 'Value', align: 'right', mono: true, cellTooltip: (v, row) => `Raw: ${v}` },\r\n * { key: 'unit', header: 'Unit', width: 80 },\r\n * ]}\r\n * data={[\r\n * { label: 'Frequency', value: '437.500', unit: 'MHz' },\r\n * { label: 'Bandwidth', value: '25.000', unit: 'kHz' },\r\n * ]}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.label}\r\n * fields={[\r\n * { label: 'Value', value: `${row.value} ${row.unit}` },\r\n * ]}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useCallback, useMemo } from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { useBreakpoint } from \"../layout/useBreakpoint\";\r\nimport { Tooltip } from \"../overlays/Tooltip\";\r\nimport type { TooltipPlacement } from \"../overlays/Tooltip\";\r\nimport { addAlpha } from \"../../utils\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport interface DataTableColumn<T = Record<string, unknown>> {\r\n /** Key in data object */\r\n key: string;\r\n /** Column header text */\r\n header: string;\r\n /** Column width (CSS value or number for px) */\r\n width?: string | number;\r\n /** Text alignment */\r\n align?: \"left\" | \"center\" | \"right\";\r\n /** Use monospace font (good for numbers) */\r\n mono?: boolean;\r\n /** Custom cell renderer */\r\n render?: (value: unknown, row: T, index: number) => React.ReactNode;\r\n /** Hide column on mobile */\r\n hideOnMobile?: boolean;\r\n /** Tooltip for the column header (explains what this column represents) */\r\n headerTooltip?: string;\r\n /** Per-cell tooltip — return string or ReactNode, or undefined to skip */\r\n cellTooltip?: (\r\n value: unknown,\r\n row: T,\r\n index: number,\r\n ) => React.ReactNode | undefined;\r\n}\r\n\r\nexport interface DataTableProps<T = Record<string, unknown>> {\r\n /** Column definitions */\r\n columns: DataTableColumn<T>[];\r\n /** Data rows */\r\n data: T[];\r\n /** Key field for React keys (default: index) */\r\n keyField?: string;\r\n /** Group rows by this field */\r\n groupBy?: string;\r\n /** Allow expanding/collapsing groups */\r\n expandable?: boolean;\r\n /** Default expanded state for groups */\r\n defaultExpanded?: boolean;\r\n /** Striped rows */\r\n striped?: boolean;\r\n /** Compact row padding */\r\n compact?: boolean;\r\n /** Show header row */\r\n showHeader?: boolean;\r\n /** Empty state message */\r\n emptyMessage?: string;\r\n /** Row click handler */\r\n onRowClick?: (row: T, index: number) => void;\r\n /** Simple string tooltip for each row (legacy — uses native title) */\r\n rowTooltip?: (row: T) => string | undefined;\r\n /** Rich tooltip content for each row — renders in a portal-based Tooltip */\r\n rowTooltipContent?: (row: T, index: number) => React.ReactNode | undefined;\r\n /** Placement for row and cell tooltips (default: 'top') */\r\n tooltipPlacement?: TooltipPlacement;\r\n /** Delay before tooltips appear in ms (default: 300) */\r\n tooltipDelay?: number;\r\n /** Max height with scroll */\r\n maxHeight?: string | number;\r\n /** Color for boolean ✓/✗ values (e.g. '#fff' for white). Defaults to text.primary or row status. */\r\n booleanColor?: string;\r\n /** Optional row status key or getter — boolean cells use this row's status color when set (matches white/status design). */\r\n getRowStatus?: (\r\n row: T,\r\n index: number,\r\n ) =>\r\n | \"normal\"\r\n | \"caution\"\r\n | \"serious\"\r\n | \"critical\"\r\n | \"standby\"\r\n | \"off\"\r\n | undefined;\r\n /** Custom style */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n// ─── DataTableRowDetail ──────────────────────────────────────────────────────\r\n\r\nexport interface DataTableRowDetailField {\r\n /** Field label */\r\n label: string;\r\n /** Field value (string, number, or ReactNode for custom rendering) */\r\n value: React.ReactNode;\r\n /** Optional status color for the value */\r\n status?: \"normal\" | \"caution\" | \"serious\" | \"critical\" | \"standby\" | \"off\";\r\n /** Use monospace font for this value */\r\n mono?: boolean;\r\n}\r\n\r\nexport interface DataTableRowDetailProps {\r\n /** Title shown at the top of the tooltip */\r\n title?: React.ReactNode;\r\n /** Optional subtitle / secondary info */\r\n subtitle?: React.ReactNode;\r\n /** Structured key-value fields to display */\r\n fields?: DataTableRowDetailField[];\r\n /** Optional footer content (timestamps, actions, etc.) */\r\n footer?: React.ReactNode;\r\n /** Optional icon or status indicator beside the title */\r\n icon?: React.ReactNode;\r\n /** Maximum width of the tooltip (default: 320) */\r\n maxWidth?: number;\r\n}\r\n\r\n/**\r\n * Pre-built tooltip content renderer for DataTable rows.\r\n * Provides a structured layout: title, key-value fields, and optional footer.\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={columns}\r\n * data={data}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.parameter}\r\n * subtitle={row.category}\r\n * fields={[\r\n * { label: 'Value', value: row.value, mono: true },\r\n * { label: 'Status', value: row.status, status: row.status },\r\n * ]}\r\n * footer={`Last updated: ${row.timestamp}`}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\nexport const DataTableRowDetail = memo(function DataTableRowDetail({\r\n title,\r\n subtitle,\r\n fields,\r\n footer,\r\n icon,\r\n maxWidth = 320,\r\n}: DataTableRowDetailProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n\r\n return (\r\n <div style={{ maxWidth, minWidth: 180 }}>\r\n {/* Header */}\r\n {(title || icon) && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n marginBottom: fields?.length || subtitle ? tokens.spacing.sm : 0,\r\n }}\r\n >\r\n {icon}\r\n <div style={{ flex: 1, minWidth: 0 }}>\r\n {title && (\r\n <div\r\n style={{\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n fontSize: tokens.typography.fontSize.sm,\r\n color: tokens.colors.text.primary,\r\n lineHeight: 1.3,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n }}\r\n >\r\n {title}\r\n </div>\r\n )}\r\n {subtitle && (\r\n <div\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n marginTop: 2,\r\n }}\r\n >\r\n {subtitle}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Fields */}\r\n {fields && fields.length > 0 && (\r\n <div\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: \"auto 1fr\",\r\n gap: `${tokens.spacing.xs} ${tokens.spacing.md}`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n lineHeight: 1.5,\r\n }}\r\n >\r\n {fields.map((field, i) => (\r\n <React.Fragment key={i}>\r\n <span\r\n style={{\r\n color: tokens.colors.text.tertiary,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.label}\r\n </span>\r\n <span\r\n style={{\r\n color: field.status\r\n ? tokens.colors.status[field.status] ||\r\n tokens.colors.text.primary\r\n : tokens.colors.text.primary,\r\n fontFamily: field.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: field.mono ? \"tabular-nums\" : \"normal\",\r\n textAlign: \"right\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.value}\r\n </span>\r\n </React.Fragment>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Footer */}\r\n {footer && (\r\n <div\r\n style={{\r\n marginTop: tokens.spacing.sm,\r\n paddingTop: tokens.spacing.xs,\r\n borderTop: `1px solid ${addAlpha(tokens.colors.border.muted, 0.19)}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n }}\r\n >\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Cell Tooltip Wrapper ────────────────────────────────────────────────────\r\n\r\ninterface CellTooltipWrapperProps {\r\n content: React.ReactNode;\r\n placement: TooltipPlacement;\r\n delay: number;\r\n children: React.ReactElement;\r\n}\r\n\r\nconst CellTooltipWrapper = memo(function CellTooltipWrapper({\r\n content,\r\n placement,\r\n delay,\r\n children,\r\n}: CellTooltipWrapperProps): React.ReactElement {\r\n if (!content) return children;\r\n return (\r\n <Tooltip content={content} placement={placement} delay={delay}>\r\n {children}\r\n </Tooltip>\r\n );\r\n});\r\n\r\n// ─── DataTable ───────────────────────────────────────────────────────────────\r\n\r\nexport const DataTable = memo(function DataTable<\r\n T extends Record<string, unknown>,\r\n>({\r\n columns,\r\n data,\r\n keyField,\r\n groupBy,\r\n expandable = false,\r\n defaultExpanded = true,\r\n striped = false,\r\n compact = false,\r\n showHeader = true,\r\n emptyMessage = \"No data available\",\r\n onRowClick,\r\n rowTooltip,\r\n rowTooltipContent,\r\n tooltipPlacement = \"top\",\r\n tooltipDelay = 300,\r\n maxHeight,\r\n booleanColor,\r\n getRowStatus,\r\n style,\r\n}: DataTableProps<T>): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const { isMobile: isMobileView } = useBreakpoint();\r\n const statusColorMap = useMemo(\r\n () => ({\r\n normal: tokens.colors.status.normal,\r\n caution: tokens.colors.status.caution,\r\n serious: tokens.colors.status.serious,\r\n critical: tokens.colors.status.critical,\r\n standby: tokens.colors.status.standby,\r\n off: tokens.colors.status.off,\r\n }),\r\n [tokens.colors.status],\r\n );\r\n const visibleColumns = isMobileView\r\n ? columns.filter((col) => !col.hideOnMobile)\r\n : columns;\r\n\r\n const [expandedGroups, setExpandedGroups] = useState<Set<string>>(\r\n defaultExpanded ? new Set([\"__all__\"]) : new Set(),\r\n );\r\n const [hoveredRow, setHoveredRow] = useState<number | null>(null);\r\n const visibleColumnCount = Math.max(1, visibleColumns.length);\r\n\r\n const cellPadding = compact\r\n ? `${tokens.spacing.xs} ${tokens.spacing.sm}`\r\n : `${tokens.spacing.sm} ${tokens.spacing.md}`;\r\n\r\n const toggleGroup = useCallback((group: string) => {\r\n setExpandedGroups((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(group)) next.delete(group);\r\n else next.add(group);\r\n return next;\r\n });\r\n }, []);\r\n\r\n const isGroupExpanded = useCallback(\r\n (group: string) => {\r\n return expandedGroups.has(\"__all__\") || expandedGroups.has(group);\r\n },\r\n [expandedGroups],\r\n );\r\n\r\n const groups = useMemo(() => {\r\n if (!groupBy) return [{ key: \"__default__\", label: \"\", rows: data }];\r\n\r\n const map = new Map<string, T[]>();\r\n data.forEach((row) => {\r\n const key = String((row as Record<string, unknown>)[groupBy] ?? \"Other\");\r\n if (!map.has(key)) map.set(key, []);\r\n map.get(key)!.push(row);\r\n });\r\n\r\n return Array.from(map.entries()).map(([key, rows]) => ({\r\n key,\r\n label: key,\r\n rows,\r\n }));\r\n }, [data, groupBy]);\r\n\r\n const resolveWidth = (w: string | number | undefined): string | undefined => {\r\n if (w === undefined) return undefined;\r\n if (typeof w === \"number\") return `${w}px`;\r\n return w;\r\n };\r\n\r\n const renderCell = (col: DataTableColumn<T>, row: T, rowIndex: number) => {\r\n const value = row[col.key];\r\n if (col.render) return col.render(value, row, rowIndex);\r\n if (value === null || value === undefined) return \"—\";\r\n if (typeof value === \"boolean\") {\r\n const label = value ? \"✓\" : \"✗\";\r\n const color =\r\n booleanColor ??\r\n (getRowStatus\r\n ? (() => {\r\n const s = getRowStatus(row, rowIndex);\r\n return s ? statusColorMap[s] : undefined;\r\n })()\r\n : undefined) ??\r\n tokens.colors.text.primary;\r\n return <span style={{ color }}>{label}</span>;\r\n }\r\n return String(value);\r\n };\r\n\r\n const renderCellWithTooltip = (\r\n col: DataTableColumn<T>,\r\n row: T,\r\n rowIndex: number,\r\n ) => {\r\n const cellContent = renderCell(col, row, rowIndex);\r\n if (!col.cellTooltip) return cellContent;\r\n\r\n const tooltipContent = col.cellTooltip(row[col.key], row, rowIndex);\r\n if (!tooltipContent) return cellContent;\r\n\r\n return (\r\n <CellTooltipWrapper\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n >\r\n <span style={{ cursor: \"help\" }}>{cellContent}</span>\r\n </CellTooltipWrapper>\r\n );\r\n };\r\n\r\n const renderHeaderCell = (col: DataTableColumn<T>) => {\r\n const headerContent = (\r\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 4 }}>\r\n {col.header}\r\n {col.headerTooltip && (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n style={{ opacity: 0.5, flexShrink: 0 }}\r\n >\r\n <circle\r\n cx=\"8\"\r\n cy=\"8\"\r\n r=\"7\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n />\r\n <path\r\n d=\"M8 7v4\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n <circle cx=\"8\" cy=\"5\" r=\"0.75\" fill=\"currentColor\" />\r\n </svg>\r\n )}\r\n </span>\r\n );\r\n\r\n if (!col.headerTooltip) return headerContent;\r\n\r\n return (\r\n <Tooltip\r\n content={col.headerTooltip}\r\n placement=\"bottom\"\r\n delay={tooltipDelay}\r\n >\r\n {headerContent}\r\n </Tooltip>\r\n );\r\n };\r\n\r\n const chevron = (expanded: boolean) => (\r\n <svg\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n style={{\r\n transition: tokens.animation.fast,\r\n transform: expanded ? \"rotate(90deg)\" : \"rotate(0deg)\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <polyline points=\"9 18 15 12 9 6\" />\r\n </svg>\r\n );\r\n\r\n const hasRichRowTooltip = !!rowTooltipContent;\r\n\r\n const renderRowContent = (row: T, rowIdx: number, localIndex: number) => {\r\n const isHovered = hoveredRow === rowIdx;\r\n const rowKey = keyField ? String(row[keyField]) : rowIdx;\r\n const useNativeTitle = !hasRichRowTooltip && rowTooltip;\r\n\r\n const tr = (\r\n <tr\r\n key={rowKey}\r\n tabIndex={onRowClick ? 0 : undefined}\r\n onClick={onRowClick ? () => onRowClick(row, rowIdx) : undefined}\r\n onKeyDown={\r\n onRowClick\r\n ? (e) => {\r\n if (e.key === \"Enter\" || e.key === \" \") {\r\n e.preventDefault();\r\n onRowClick(row, rowIdx);\r\n }\r\n }\r\n : undefined\r\n }\r\n onMouseEnter={() => setHoveredRow(rowIdx)}\r\n onMouseLeave={() => setHoveredRow(null)}\r\n title={useNativeTitle ? rowTooltip(row) : undefined}\r\n style={{\r\n cursor: onRowClick ? \"pointer\" : \"default\",\r\n backgroundColor: isHovered\r\n ? addAlpha(tokens.colors.accent.primary, 0.08)\r\n : striped && localIndex % 2 === 1\r\n ? addAlpha(tokens.colors.background.surface, 0.5)\r\n : \"transparent\",\r\n transition: \"background-color 150ms ease\",\r\n }}\r\n >\r\n {visibleColumns.map((col) => (\r\n <td\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n color: tokens.colors.text.primary,\r\n borderBottom: `1px solid ${addAlpha(tokens.colors.border.muted, 0.08)}`,\r\n fontFamily: col.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: col.mono ? \"tabular-nums\" : \"normal\",\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n maxWidth: resolveWidth(col.width) || \"300px\",\r\n }}\r\n >\r\n {renderCellWithTooltip(col, row, rowIdx)}\r\n </td>\r\n ))}\r\n </tr>\r\n );\r\n\r\n if (hasRichRowTooltip) {\r\n const tooltipContent = rowTooltipContent(row, rowIdx);\r\n if (tooltipContent) {\r\n return (\r\n <Tooltip\r\n key={rowKey}\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n >\r\n {tr}\r\n </Tooltip>\r\n );\r\n }\r\n }\r\n\r\n return tr;\r\n };\r\n\r\n let globalRowIndex = 0;\r\n\r\n return (\r\n <div\r\n style={{\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n overflow: \"auto\",\r\n WebkitOverflowScrolling: \"touch\",\r\n maxHeight: typeof maxHeight === \"number\" ? `${maxHeight}px` : maxHeight,\r\n borderRadius: tokens.borderRadius.md,\r\n border: tokens.borders.divider,\r\n ...style,\r\n }}\r\n >\r\n <table\r\n style={{\r\n width: \"100%\",\r\n borderCollapse: \"collapse\",\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {showHeader && (\r\n <thead>\r\n <tr>\r\n {visibleColumns.map((col) => (\r\n <th\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: tokens.typography.fontWeight.bold,\r\n color: tokens.colors.text.tertiary,\r\n textTransform: \"uppercase\",\r\n letterSpacing: \"0.06em\",\r\n backgroundColor: tokens.colors.background.surface,\r\n borderBottom: tokens.borders.divider,\r\n width: resolveWidth(col.width),\r\n position: \"sticky\",\r\n top: 0,\r\n zIndex: 1,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {renderHeaderCell(col)}\r\n </th>\r\n ))}\r\n </tr>\r\n </thead>\r\n )}\r\n\r\n <tbody>\r\n {data.length === 0 && (\r\n <tr>\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: tokens.spacing.xl,\r\n textAlign: \"center\",\r\n color: tokens.colors.text.tertiary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {emptyMessage}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {groups.map((group) => {\r\n const isExpanded = !groupBy || isGroupExpanded(group.key);\r\n\r\n return (\r\n <React.Fragment key={group.key}>\r\n {groupBy && group.label && (\r\n <tr\r\n style={{\r\n backgroundColor: addAlpha(\r\n tokens.colors.accent.primary,\r\n 0.08,\r\n ),\r\n }}\r\n >\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: 0,\r\n borderBottom: tokens.borders.divider,\r\n }}\r\n >\r\n {expandable ? (\r\n <button\r\n type=\"button\"\r\n aria-expanded={isExpanded}\r\n onClick={() => toggleGroup(group.key)}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n width: \"100%\",\r\n padding: cellPadding,\r\n margin: 0,\r\n border: \"none\",\r\n background: \"none\",\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n fontFamily: \"inherit\",\r\n textAlign: \"left\",\r\n }}\r\n >\r\n {chevron(isExpanded)}\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </button>\r\n ) : (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n padding: cellPadding,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </div>\r\n )}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {isExpanded &&\r\n group.rows.map((row, localIndex) => {\r\n const rowIdx = globalRowIndex++;\r\n return renderRowContent(row, rowIdx, localIndex);\r\n })}\r\n </React.Fragment>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n}) as <T extends Record<string, unknown>>(\r\n props: DataTableProps<T>,\r\n) => React.ReactElement;\r\n\r\nexport default DataTable;\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../../src/react/core/data/DataTable.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - DataTable Component\r\n *\r\n * Structured data display with expandable groups, responsive columns,\r\n * and theme-aware styling. Designed for telemetry parameter grids,\r\n * link budget displays, spacecraft information panels.\r\n *\r\n * Astro UX Compliance:\r\n * - Tabular monospace numbers for value columns\r\n * - Status-based row coloring\r\n * - Compact mode support\r\n * - Accessible table semantics\r\n *\r\n * Enhanced Tooltip System:\r\n * - Row-level tooltips (string or rich ReactNode via rowTooltipContent)\r\n * - Cell-level tooltips per column definition\r\n * - Header tooltips for column descriptions\r\n * - Built-in DataTableRowDetail component for structured detail views\r\n * - Portal-rendered, WCAG-compliant, animated tooltips\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={[\r\n * { key: 'label', header: 'Parameter', width: '40%', headerTooltip: 'Telemetry parameter name' },\r\n * { key: 'value', header: 'Value', align: 'right', mono: true, cellTooltip: (v, row) => `Raw: ${v}` },\r\n * { key: 'unit', header: 'Unit', width: 80 },\r\n * ]}\r\n * data={[\r\n * { label: 'Frequency', value: '437.500', unit: 'MHz' },\r\n * { label: 'Bandwidth', value: '25.000', unit: 'kHz' },\r\n * ]}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.label}\r\n * fields={[\r\n * { label: 'Value', value: `${row.value} ${row.unit}` },\r\n * ]}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useCallback, useMemo } from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { useBreakpoint } from \"../layout/useBreakpoint\";\r\nimport { Tooltip } from \"../overlays/Tooltip\";\r\nimport type { TooltipPlacement } from \"../overlays/Tooltip\";\r\nimport { addAlpha } from \"../../utils\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport interface DataTableColumn<T = Record<string, unknown>> {\r\n /** Key in data object */\r\n key: string;\r\n /** Column header text */\r\n header: string;\r\n /** Column width (CSS value or number for px) */\r\n width?: string | number;\r\n /** Text alignment */\r\n align?: \"left\" | \"center\" | \"right\";\r\n /** Use monospace font (good for numbers) */\r\n mono?: boolean;\r\n /** Custom cell renderer */\r\n render?: (value: unknown, row: T, index: number) => React.ReactNode;\r\n /** Hide column on mobile */\r\n hideOnMobile?: boolean;\r\n /** Tooltip for the column header (explains what this column represents) */\r\n headerTooltip?: string;\r\n /** Per-cell tooltip — return string or ReactNode, or undefined to skip */\r\n cellTooltip?: (\r\n value: unknown,\r\n row: T,\r\n index: number,\r\n ) => React.ReactNode | undefined;\r\n}\r\n\r\nexport interface DataTableProps<T = Record<string, unknown>> {\r\n /** Column definitions */\r\n columns: DataTableColumn<T>[];\r\n /** Data rows */\r\n data: T[];\r\n /** Key field for React keys (default: index) */\r\n keyField?: string;\r\n /** Group rows by this field */\r\n groupBy?: string;\r\n /** Allow expanding/collapsing groups */\r\n expandable?: boolean;\r\n /** Default expanded state for groups */\r\n defaultExpanded?: boolean;\r\n /** Striped rows */\r\n striped?: boolean;\r\n /** Compact row padding */\r\n compact?: boolean;\r\n /** Show header row */\r\n showHeader?: boolean;\r\n /** Empty state message */\r\n emptyMessage?: string;\r\n /** Row click handler */\r\n onRowClick?: (row: T, index: number) => void;\r\n /** Simple string tooltip for each row (legacy — uses native title) */\r\n rowTooltip?: (row: T) => string | undefined;\r\n /** Rich tooltip content for each row — renders in a portal-based Tooltip */\r\n rowTooltipContent?: (row: T, index: number) => React.ReactNode | undefined;\r\n /** Placement for row and cell tooltips (default: 'top') */\r\n tooltipPlacement?: TooltipPlacement;\r\n /** Delay before tooltips appear in ms (default: 300) */\r\n tooltipDelay?: number;\r\n /** Max height with scroll */\r\n maxHeight?: string | number;\r\n /** Color for boolean ✓/✗ values (e.g. '#fff' for white). Defaults to text.primary or row status. */\r\n booleanColor?: string;\r\n /** Optional row status key or getter — boolean cells use this row's status color when set (matches white/status design). */\r\n getRowStatus?: (\r\n row: T,\r\n index: number,\r\n ) =>\r\n | \"normal\"\r\n | \"caution\"\r\n | \"serious\"\r\n | \"critical\"\r\n | \"standby\"\r\n | \"off\"\r\n | undefined;\r\n /** Custom style */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n// ─── DataTableRowDetail ──────────────────────────────────────────────────────\r\n\r\nexport interface DataTableRowDetailField {\r\n /** Field label */\r\n label: string;\r\n /** Field value (string, number, or ReactNode for custom rendering) */\r\n value: React.ReactNode;\r\n /** Optional status color for the value */\r\n status?: \"normal\" | \"caution\" | \"serious\" | \"critical\" | \"standby\" | \"off\";\r\n /** Use monospace font for this value */\r\n mono?: boolean;\r\n}\r\n\r\nexport interface DataTableRowDetailProps {\r\n /** Title shown at the top of the tooltip */\r\n title?: React.ReactNode;\r\n /** Optional subtitle / secondary info */\r\n subtitle?: React.ReactNode;\r\n /** Structured key-value fields to display */\r\n fields?: DataTableRowDetailField[];\r\n /** Optional footer content (timestamps, actions, etc.) */\r\n footer?: React.ReactNode;\r\n /** Optional icon or status indicator beside the title */\r\n icon?: React.ReactNode;\r\n /** Maximum width of the tooltip (default: 320) */\r\n maxWidth?: number;\r\n}\r\n\r\n/**\r\n * Pre-built tooltip content renderer for DataTable rows.\r\n * Provides a structured layout: title, key-value fields, and optional footer.\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={columns}\r\n * data={data}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.parameter}\r\n * subtitle={row.category}\r\n * fields={[\r\n * { label: 'Value', value: row.value, mono: true },\r\n * { label: 'Status', value: row.status, status: row.status },\r\n * ]}\r\n * footer={`Last updated: ${row.timestamp}`}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\nexport const DataTableRowDetail = memo(function DataTableRowDetail({\r\n title,\r\n subtitle,\r\n fields,\r\n footer,\r\n icon,\r\n maxWidth = 320,\r\n}: DataTableRowDetailProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n\r\n return (\r\n <div style={{ maxWidth, minWidth: 180 }}>\r\n {/* Header */}\r\n {(title || icon) && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n marginBottom: fields?.length || subtitle ? tokens.spacing.sm : 0,\r\n }}\r\n >\r\n {icon}\r\n <div style={{ flex: 1, minWidth: 0 }}>\r\n {title && (\r\n <div\r\n style={{\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n fontSize: tokens.typography.fontSize.sm,\r\n color: tokens.colors.text.primary,\r\n lineHeight: 1.3,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n }}\r\n >\r\n {title}\r\n </div>\r\n )}\r\n {subtitle && (\r\n <div\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n marginTop: 2,\r\n }}\r\n >\r\n {subtitle}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Fields */}\r\n {fields && fields.length > 0 && (\r\n <div\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: \"auto 1fr\",\r\n gap: `${tokens.spacing.xs} ${tokens.spacing.md}`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n lineHeight: 1.5,\r\n }}\r\n >\r\n {fields.map((field, i) => (\r\n <React.Fragment key={i}>\r\n <span\r\n style={{\r\n color: tokens.colors.text.tertiary,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.label}\r\n </span>\r\n <span\r\n style={{\r\n color: field.status\r\n ? tokens.colors.status[field.status] ||\r\n tokens.colors.text.primary\r\n : tokens.colors.text.primary,\r\n fontFamily: field.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: field.mono ? \"tabular-nums\" : \"normal\",\r\n textAlign: \"right\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.value}\r\n </span>\r\n </React.Fragment>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Footer */}\r\n {footer && (\r\n <div\r\n style={{\r\n marginTop: tokens.spacing.sm,\r\n paddingTop: tokens.spacing.xs,\r\n borderTop: `1px solid ${addAlpha(tokens.colors.border.muted, 0.19)}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n }}\r\n >\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Cell Tooltip Wrapper ────────────────────────────────────────────────────\r\n\r\ninterface CellTooltipWrapperProps {\r\n content: React.ReactNode;\r\n placement: TooltipPlacement;\r\n delay: number;\r\n children: React.ReactElement;\r\n}\r\n\r\nconst CellTooltipWrapper = memo(function CellTooltipWrapper({\r\n content,\r\n placement,\r\n delay,\r\n children,\r\n}: CellTooltipWrapperProps): React.ReactElement {\r\n if (!content) return children;\r\n return (\r\n <Tooltip content={content} placement={placement} delay={delay}>\r\n {children}\r\n </Tooltip>\r\n );\r\n});\r\n\r\n// ─── DataTable ───────────────────────────────────────────────────────────────\r\n\r\nexport const DataTable = memo(function DataTable<\r\n T extends Record<string, unknown>,\r\n>({\r\n columns,\r\n data,\r\n keyField,\r\n groupBy,\r\n expandable = false,\r\n defaultExpanded = true,\r\n striped = false,\r\n compact = false,\r\n showHeader = true,\r\n emptyMessage = \"No data available\",\r\n onRowClick,\r\n rowTooltip,\r\n rowTooltipContent,\r\n tooltipPlacement = \"top\",\r\n tooltipDelay = 300,\r\n maxHeight,\r\n booleanColor,\r\n getRowStatus,\r\n style,\r\n}: DataTableProps<T>): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const { isMobile: isMobileView } = useBreakpoint();\r\n const statusColorMap = useMemo(\r\n () => ({\r\n normal: tokens.colors.status.normal,\r\n caution: tokens.colors.status.caution,\r\n serious: tokens.colors.status.serious,\r\n critical: tokens.colors.status.critical,\r\n standby: tokens.colors.status.standby,\r\n off: tokens.colors.status.off,\r\n }),\r\n [tokens.colors.status],\r\n );\r\n const visibleColumns = isMobileView\r\n ? columns.filter((col) => !col.hideOnMobile)\r\n : columns;\r\n\r\n const [expandedGroups, setExpandedGroups] = useState<Set<string>>(\r\n defaultExpanded ? new Set([\"__all__\"]) : new Set(),\r\n );\r\n const [hoveredRow, setHoveredRow] = useState<number | null>(null);\r\n const visibleColumnCount = Math.max(1, visibleColumns.length);\r\n\r\n const cellPadding = compact\r\n ? `${tokens.spacing.xs} ${tokens.spacing.sm}`\r\n : `${tokens.spacing.sm} ${tokens.spacing.md}`;\r\n\r\n const toggleGroup = useCallback((group: string) => {\r\n setExpandedGroups((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(group)) next.delete(group);\r\n else next.add(group);\r\n return next;\r\n });\r\n }, []);\r\n\r\n const isGroupExpanded = useCallback(\r\n (group: string) => {\r\n return expandedGroups.has(\"__all__\") || expandedGroups.has(group);\r\n },\r\n [expandedGroups],\r\n );\r\n\r\n const groups = useMemo(() => {\r\n if (!groupBy) return [{ key: \"__default__\", label: \"\", rows: data }];\r\n\r\n const map = new Map<string, T[]>();\r\n data.forEach((row) => {\r\n const key = String((row as Record<string, unknown>)[groupBy] ?? \"Other\");\r\n if (!map.has(key)) map.set(key, []);\r\n map.get(key)!.push(row);\r\n });\r\n\r\n return Array.from(map.entries()).map(([key, rows]) => ({\r\n key,\r\n label: key,\r\n rows,\r\n }));\r\n }, [data, groupBy]);\r\n\r\n const resolveWidth = (w: string | number | undefined): string | undefined => {\r\n if (w === undefined) return undefined;\r\n if (typeof w === \"number\") return `${w}px`;\r\n return w;\r\n };\r\n\r\n const renderCell = (col: DataTableColumn<T>, row: T, rowIndex: number) => {\r\n const value = row[col.key];\r\n if (col.render) return col.render(value, row, rowIndex);\r\n if (value === null || value === undefined) return \"—\";\r\n if (typeof value === \"boolean\") {\r\n const label = value ? \"✓\" : \"✗\";\r\n const color =\r\n booleanColor ??\r\n (getRowStatus\r\n ? (() => {\r\n const s = getRowStatus(row, rowIndex);\r\n return s ? statusColorMap[s] : undefined;\r\n })()\r\n : undefined) ??\r\n tokens.colors.text.primary;\r\n return <span style={{ color }}>{label}</span>;\r\n }\r\n return String(value);\r\n };\r\n\r\n const renderCellWithTooltip = (\r\n col: DataTableColumn<T>,\r\n row: T,\r\n rowIndex: number,\r\n ) => {\r\n const cellContent = renderCell(col, row, rowIndex);\r\n if (!col.cellTooltip) return cellContent;\r\n\r\n const tooltipContent = col.cellTooltip(row[col.key], row, rowIndex);\r\n if (!tooltipContent) return cellContent;\r\n\r\n return (\r\n <CellTooltipWrapper\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n >\r\n <span style={{ cursor: \"help\" }}>{cellContent}</span>\r\n </CellTooltipWrapper>\r\n );\r\n };\r\n\r\n const renderHeaderCell = (col: DataTableColumn<T>) => {\r\n const headerContent = (\r\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 4 }}>\r\n {col.header}\r\n {col.headerTooltip && (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n style={{ opacity: 0.5, flexShrink: 0 }}\r\n >\r\n <circle\r\n cx=\"8\"\r\n cy=\"8\"\r\n r=\"7\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n />\r\n <path\r\n d=\"M8 7v4\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n <circle cx=\"8\" cy=\"5\" r=\"0.75\" fill=\"currentColor\" />\r\n </svg>\r\n )}\r\n </span>\r\n );\r\n\r\n if (!col.headerTooltip) return headerContent;\r\n\r\n return (\r\n <Tooltip\r\n content={col.headerTooltip}\r\n placement=\"bottom\"\r\n delay={tooltipDelay}\r\n >\r\n {headerContent}\r\n </Tooltip>\r\n );\r\n };\r\n\r\n const chevron = (expanded: boolean) => (\r\n <svg\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n style={{\r\n transition: tokens.animation.fast,\r\n transform: expanded ? \"rotate(90deg)\" : \"rotate(0deg)\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <polyline points=\"9 18 15 12 9 6\" />\r\n </svg>\r\n );\r\n\r\n const hasRichRowTooltip = !!rowTooltipContent;\r\n\r\n const renderRowContent = (row: T, rowIdx: number, localIndex: number) => {\r\n const isHovered = hoveredRow === rowIdx;\r\n const rowKey = keyField ? String(row[keyField]) : rowIdx;\r\n const useNativeTitle = !hasRichRowTooltip && rowTooltip;\r\n\r\n const tr = (\r\n <tr\r\n key={rowKey}\r\n tabIndex={onRowClick ? 0 : undefined}\r\n onClick={onRowClick ? () => onRowClick(row, rowIdx) : undefined}\r\n onKeyDown={\r\n onRowClick\r\n ? (e) => {\r\n if (e.key === \"Enter\" || e.key === \" \") {\r\n e.preventDefault();\r\n onRowClick(row, rowIdx);\r\n }\r\n }\r\n : undefined\r\n }\r\n onMouseEnter={() => setHoveredRow(rowIdx)}\r\n onMouseLeave={() => setHoveredRow(null)}\r\n title={useNativeTitle ? rowTooltip(row) : undefined}\r\n style={{\r\n cursor: onRowClick ? \"pointer\" : \"default\",\r\n backgroundColor: isHovered\r\n ? addAlpha(tokens.colors.accent.primary, 0.08)\r\n : striped && localIndex % 2 === 1\r\n ? addAlpha(tokens.colors.background.surface, 0.5)\r\n : \"transparent\",\r\n transition: \"background-color 150ms ease\",\r\n }}\r\n >\r\n {visibleColumns.map((col) => (\r\n <td\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n color: tokens.colors.text.primary,\r\n borderBottom: `1px solid ${addAlpha(tokens.colors.border.muted, 0.08)}`,\r\n fontFamily: col.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: col.mono ? \"tabular-nums\" : \"normal\",\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n maxWidth: resolveWidth(col.width) || \"300px\",\r\n }}\r\n >\r\n {renderCellWithTooltip(col, row, rowIdx)}\r\n </td>\r\n ))}\r\n </tr>\r\n );\r\n\r\n if (hasRichRowTooltip) {\r\n const tooltipContent = rowTooltipContent(row, rowIdx);\r\n if (tooltipContent) {\r\n return (\r\n <Tooltip\r\n key={rowKey}\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n // Attached to the row, never wrapped around it. A wrapper here\r\n // is a div between `tbody` and `tr`, which is not a table row —\r\n // the row stops taking part in the table's layout and its cells\r\n // reflow as inline text, taking the whole table apart.\r\n asChild\r\n >\r\n {tr}\r\n </Tooltip>\r\n );\r\n }\r\n }\r\n\r\n return tr;\r\n };\r\n\r\n let globalRowIndex = 0;\r\n\r\n return (\r\n <div\r\n style={{\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n overflow: \"auto\",\r\n WebkitOverflowScrolling: \"touch\",\r\n maxHeight: typeof maxHeight === \"number\" ? `${maxHeight}px` : maxHeight,\r\n borderRadius: tokens.borderRadius.md,\r\n border: tokens.borders.divider,\r\n ...style,\r\n }}\r\n >\r\n <table\r\n style={{\r\n width: \"100%\",\r\n borderCollapse: \"collapse\",\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {showHeader && (\r\n <thead>\r\n <tr>\r\n {visibleColumns.map((col) => (\r\n <th\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: tokens.typography.fontWeight.bold,\r\n color: tokens.colors.text.tertiary,\r\n textTransform: \"uppercase\",\r\n letterSpacing: \"0.06em\",\r\n backgroundColor: tokens.colors.background.surface,\r\n borderBottom: tokens.borders.divider,\r\n width: resolveWidth(col.width),\r\n position: \"sticky\",\r\n top: 0,\r\n zIndex: 1,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {renderHeaderCell(col)}\r\n </th>\r\n ))}\r\n </tr>\r\n </thead>\r\n )}\r\n\r\n <tbody>\r\n {data.length === 0 && (\r\n <tr>\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: tokens.spacing.xl,\r\n textAlign: \"center\",\r\n color: tokens.colors.text.tertiary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {emptyMessage}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {groups.map((group) => {\r\n const isExpanded = !groupBy || isGroupExpanded(group.key);\r\n\r\n return (\r\n <React.Fragment key={group.key}>\r\n {groupBy && group.label && (\r\n <tr\r\n style={{\r\n backgroundColor: addAlpha(\r\n tokens.colors.accent.primary,\r\n 0.08,\r\n ),\r\n }}\r\n >\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: 0,\r\n borderBottom: tokens.borders.divider,\r\n }}\r\n >\r\n {expandable ? (\r\n <button\r\n type=\"button\"\r\n aria-expanded={isExpanded}\r\n onClick={() => toggleGroup(group.key)}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n width: \"100%\",\r\n padding: cellPadding,\r\n margin: 0,\r\n border: \"none\",\r\n background: \"none\",\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n fontFamily: \"inherit\",\r\n textAlign: \"left\",\r\n }}\r\n >\r\n {chevron(isExpanded)}\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </button>\r\n ) : (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n padding: cellPadding,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </div>\r\n )}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {isExpanded &&\r\n group.rows.map((row, localIndex) => {\r\n const rowIdx = globalRowIndex++;\r\n return renderRowContent(row, rowIdx, localIndex);\r\n })}\r\n </React.Fragment>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n}) as <T extends Record<string, unknown>>(\r\n props: DataTableProps<T>,\r\n) => React.ReactElement;\r\n\r\nexport default DataTable;\r\n"],"mappings":"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"}
|
|
@@ -11,6 +11,20 @@ export interface TooltipProps {
|
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
/** Children element to trigger tooltip */
|
|
13
13
|
children: React.ReactElement;
|
|
14
|
+
/**
|
|
15
|
+
* Attach to the child rather than wrapping it.
|
|
16
|
+
*
|
|
17
|
+
* The wrapper is a `div`, which most of the time is invisible and fine.
|
|
18
|
+
* Somewhere its parent dictates what its children may be it is neither: a
|
|
19
|
+
* `div` between `tbody` and `tr` is not a table row, so the row it holds
|
|
20
|
+
* stops taking part in the table's layout and its cells reflow as inline
|
|
21
|
+
* content — the table comes apart. Cloning the child to carry the
|
|
22
|
+
* handlers and the ref leaves the markup its parent requires intact.
|
|
23
|
+
*
|
|
24
|
+
* The child has to take a ref and spread the handlers, which a DOM
|
|
25
|
+
* element does and an arbitrary component may not.
|
|
26
|
+
*/
|
|
27
|
+
asChild?: boolean;
|
|
14
28
|
/** Custom className */
|
|
15
29
|
className?: string;
|
|
16
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTheme } from "../../theme/ThemeProvider.js";
|
|
2
2
|
import { classNames } from "../../utils/index.js";
|
|
3
|
-
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import React, { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import ReactDOM from "react-dom";
|
|
6
6
|
//#region src/react/core/overlays/Tooltip.tsx
|
|
@@ -18,7 +18,7 @@ import ReactDOM from "react-dom";
|
|
|
18
18
|
* </Tooltip>
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
var Tooltip = memo(function Tooltip({ content, placement = "top", delay = 200, disabled = false, children, className = "" }) {
|
|
21
|
+
var Tooltip = memo(function Tooltip({ content, placement = "top", delay = 200, disabled = false, children, className = "", asChild = false }) {
|
|
22
22
|
const { tokens } = useTheme();
|
|
23
23
|
const [visible, setVisible] = useState(false);
|
|
24
24
|
const [positioned, setPositioned] = useState(false);
|
|
@@ -120,7 +120,23 @@ var Tooltip = memo(function Tooltip({ content, placement = "top", delay = 200, d
|
|
|
120
120
|
if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
|
|
121
121
|
};
|
|
122
122
|
}, []);
|
|
123
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Composed rather than replaced: a child given these already is a child
|
|
125
|
+
* that does something with them, and taking that away to add a tooltip
|
|
126
|
+
* would be a surprising thing for a tooltip to do.
|
|
127
|
+
*/
|
|
128
|
+
const compose = (own, theirs) => (e) => {
|
|
129
|
+
theirs?.(e);
|
|
130
|
+
own();
|
|
131
|
+
};
|
|
132
|
+
const childProps = children.props;
|
|
133
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [asChild ? React.cloneElement(children, {
|
|
134
|
+
ref: triggerRef,
|
|
135
|
+
onMouseEnter: compose(showTooltip, childProps.onMouseEnter),
|
|
136
|
+
onMouseLeave: compose(hideTooltip, childProps.onMouseLeave),
|
|
137
|
+
onFocus: compose(showTooltip, childProps.onFocus),
|
|
138
|
+
onBlur: compose(hideTooltip, childProps.onBlur)
|
|
139
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
124
140
|
ref: triggerRef,
|
|
125
141
|
className: classNames("zendir-tooltip-trigger", className),
|
|
126
142
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","names":[],"sources":["../../../../src/react/core/overlays/Tooltip.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Tooltip Component\r\n *\r\n * Tooltip following Astro UX Design System.\r\n * Uses a portal to render in document.body, ensuring correct positioning\r\n * even when the trigger is inside containers with CSS transforms or overflow.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Tooltip content=\"Click to enable telemetry\">\r\n * <Button>Enable</Button>\r\n * </Tooltip>\r\n * ```\r\n */\r\n\r\nimport React, {\r\n memo,\r\n useState,\r\n useRef,\r\n useEffect,\r\n useLayoutEffect,\r\n useCallback,\r\n} from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { classNames } from \"../../utils\";\r\n\r\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\r\n\r\nexport interface TooltipProps {\r\n /** Tooltip content */\r\n content: React.ReactNode;\r\n /** Placement */\r\n placement?: TooltipPlacement;\r\n /** Delay before showing (ms) */\r\n delay?: number;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Children element to trigger tooltip */\r\n children: React.ReactElement;\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\nexport const Tooltip = memo(function Tooltip({\r\n content,\r\n placement = \"top\",\r\n delay = 200,\r\n disabled = false,\r\n children,\r\n className = \"\",\r\n}: TooltipProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const [visible, setVisible] = useState(false);\r\n const [positioned, setPositioned] = useState(false);\r\n const [position, setPosition] = useState({ top: 0, left: 0 });\r\n const triggerRef = useRef<HTMLDivElement>(null);\r\n const tooltipRef = useRef<HTMLDivElement>(null);\r\n const timeoutRef = useRef<number>();\r\n const hideTimeoutRef = useRef<number>();\r\n\r\n const showTooltip = useCallback(() => {\r\n if (disabled) return;\r\n // Cancel any pending hide so moving between trigger ↔ tooltip keeps it open\r\n if (hideTimeoutRef.current) {\r\n clearTimeout(hideTimeoutRef.current);\r\n hideTimeoutRef.current = undefined;\r\n }\r\n timeoutRef.current = window.setTimeout(() => {\r\n setVisible(true);\r\n setPositioned(false);\r\n }, delay);\r\n }, [disabled, delay]);\r\n\r\n const hideTooltip = useCallback(() => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n // Small delay so user can move pointer from trigger to tooltip (WCAG 1.4.13)\r\n hideTimeoutRef.current = window.setTimeout(() => {\r\n setVisible(false);\r\n setPositioned(false);\r\n }, 100);\r\n }, []);\r\n\r\n const hideImmediately = useCallback(() => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n setVisible(false);\r\n setPositioned(false);\r\n }, []);\r\n\r\n const calculatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n const gap = 8;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n let top = 0;\r\n let left = 0;\r\n\r\n switch (placement) {\r\n case \"top\":\r\n top = triggerRect.top - tooltipRect.height - gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"bottom\":\r\n top = triggerRect.bottom + gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"left\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.left - tooltipRect.width - gap;\r\n break;\r\n case \"right\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.right + gap;\r\n break;\r\n }\r\n\r\n // Keep tooltip within viewport bounds\r\n if (left < gap) left = gap;\r\n if (left + tooltipRect.width > viewportWidth - gap) {\r\n left = viewportWidth - tooltipRect.width - gap;\r\n }\r\n if (top < gap) top = gap;\r\n if (top + tooltipRect.height > viewportHeight - gap) {\r\n top = viewportHeight - tooltipRect.height - gap;\r\n }\r\n\r\n setPosition({ top, left });\r\n setPositioned(true);\r\n }, [placement]);\r\n\r\n // Use useLayoutEffect for initial positioning - runs synchronously\r\n // before browser paint, preventing flash at wrong position\r\n useLayoutEffect(() => {\r\n if (visible && triggerRef.current && tooltipRef.current) {\r\n calculatePosition();\r\n }\r\n }, [visible, calculatePosition]);\r\n\r\n // Use useEffect for scroll/resize listeners\r\n useEffect(() => {\r\n if (!visible) return;\r\n\r\n const handleScrollOrResize = () => {\r\n calculatePosition();\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.addEventListener(\"resize\", handleScrollOrResize);\r\n\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.removeEventListener(\"resize\", handleScrollOrResize);\r\n };\r\n }, [visible, calculatePosition]);\r\n\r\n // Escape key dismisses tooltip (WCAG 1.4.13 Content on Hover or Focus)\r\n useEffect(() => {\r\n if (!visible) return;\r\n const handleEscape = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n hideImmediately();\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleEscape);\r\n return () => document.removeEventListener(\"keydown\", handleEscape);\r\n }, [visible, hideImmediately]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n };\r\n }, []);\r\n\r\n return (\r\n <>\r\n <div\r\n ref={triggerRef}\r\n className={classNames(\"zendir-tooltip-trigger\", className)}\r\n style={{\r\n display: \"inline-flex\",\r\n pointerEvents: \"auto\",\r\n maxWidth: \"100%\",\r\n width: \"inherit\",\r\n }}\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n onFocus={showTooltip}\r\n onBlur={hideTooltip}\r\n >\r\n {children}\r\n </div>\r\n\r\n {visible &&\r\n ReactDOM.createPortal(\r\n <div\r\n ref={tooltipRef}\r\n role=\"tooltip\"\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n style={{\r\n position: \"fixed\",\r\n top: positioned ? position.top : 0,\r\n left: positioned ? position.left : 0,\r\n width: \"max-content\",\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n backgroundColor: tokens.colors.background.elevated,\r\n color: tokens.colors.text.primary,\r\n fontSize: \"0.75rem\",\r\n fontWeight: 500,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n borderRadius: tokens.borderRadius.md,\r\n border: `1px solid ${tokens.colors.accent.primary}20`,\r\n boxShadow: positioned\r\n ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10`\r\n : \"none\",\r\n zIndex: 2147483100,\r\n maxWidth: \"min(280px, calc(100vw - 24px))\",\r\n whiteSpace: \"normal\",\r\n pointerEvents: \"auto\",\r\n visibility: positioned ? \"visible\" : \"hidden\",\r\n opacity: positioned ? 1 : 0,\r\n transition: positioned ? undefined : \"none\",\r\n animation: positioned\r\n ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both`\r\n : \"none\",\r\n backdropFilter: \"blur(8px)\",\r\n }}\r\n >\r\n {content}\r\n <style>\r\n {`@keyframes zendir-tooltip { from { opacity: 0; transform: scale(0.95) translateY(${placement === \"bottom\" ? \"-4px\" : placement === \"top\" ? \"4px\" : \"0\"}); } to { opacity: 1; transform: scale(1) translateY(0); } }\r\n @media (prefers-reduced-motion: reduce) { @keyframes zendir-tooltip { from, to { opacity: 1; transform: none; } } }`}\r\n </style>\r\n </div>,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n});\r\n\r\nexport default Tooltip;\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tooltip.js","names":[],"sources":["../../../../src/react/core/overlays/Tooltip.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Tooltip Component\r\n *\r\n * Tooltip following Astro UX Design System.\r\n * Uses a portal to render in document.body, ensuring correct positioning\r\n * even when the trigger is inside containers with CSS transforms or overflow.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Tooltip content=\"Click to enable telemetry\">\r\n * <Button>Enable</Button>\r\n * </Tooltip>\r\n * ```\r\n */\r\n\r\nimport React, {\r\n memo,\r\n useState,\r\n useRef,\r\n useEffect,\r\n useLayoutEffect,\r\n useCallback,\r\n} from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { classNames } from \"../../utils\";\r\n\r\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\r\n\r\nexport interface TooltipProps {\r\n /** Tooltip content */\r\n content: React.ReactNode;\r\n /** Placement */\r\n placement?: TooltipPlacement;\r\n /** Delay before showing (ms) */\r\n delay?: number;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Children element to trigger tooltip */\r\n children: React.ReactElement;\r\n /**\r\n * Attach to the child rather than wrapping it.\r\n *\r\n * The wrapper is a `div`, which most of the time is invisible and fine.\r\n * Somewhere its parent dictates what its children may be it is neither: a\r\n * `div` between `tbody` and `tr` is not a table row, so the row it holds\r\n * stops taking part in the table's layout and its cells reflow as inline\r\n * content — the table comes apart. Cloning the child to carry the\r\n * handlers and the ref leaves the markup its parent requires intact.\r\n *\r\n * The child has to take a ref and spread the handlers, which a DOM\r\n * element does and an arbitrary component may not.\r\n */\r\n asChild?: boolean;\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\nexport const Tooltip = memo(function Tooltip({\r\n content,\r\n placement = \"top\",\r\n delay = 200,\r\n disabled = false,\r\n children,\r\n className = \"\",\r\n asChild = false,\r\n}: TooltipProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const [visible, setVisible] = useState(false);\r\n const [positioned, setPositioned] = useState(false);\r\n const [position, setPosition] = useState({ top: 0, left: 0 });\r\n // Not necessarily a div any more: with `asChild` the trigger is whatever\r\n // element the caller handed over, a table row included.\r\n const triggerRef = useRef<HTMLElement>(null);\r\n const tooltipRef = useRef<HTMLDivElement>(null);\r\n const timeoutRef = useRef<number>();\r\n const hideTimeoutRef = useRef<number>();\r\n\r\n const showTooltip = useCallback(() => {\r\n if (disabled) return;\r\n // Cancel any pending hide so moving between trigger ↔ tooltip keeps it open\r\n if (hideTimeoutRef.current) {\r\n clearTimeout(hideTimeoutRef.current);\r\n hideTimeoutRef.current = undefined;\r\n }\r\n timeoutRef.current = window.setTimeout(() => {\r\n setVisible(true);\r\n setPositioned(false);\r\n }, delay);\r\n }, [disabled, delay]);\r\n\r\n const hideTooltip = useCallback(() => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n // Small delay so user can move pointer from trigger to tooltip (WCAG 1.4.13)\r\n hideTimeoutRef.current = window.setTimeout(() => {\r\n setVisible(false);\r\n setPositioned(false);\r\n }, 100);\r\n }, []);\r\n\r\n const hideImmediately = useCallback(() => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n setVisible(false);\r\n setPositioned(false);\r\n }, []);\r\n\r\n const calculatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n const gap = 8;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n let top = 0;\r\n let left = 0;\r\n\r\n switch (placement) {\r\n case \"top\":\r\n top = triggerRect.top - tooltipRect.height - gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"bottom\":\r\n top = triggerRect.bottom + gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"left\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.left - tooltipRect.width - gap;\r\n break;\r\n case \"right\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.right + gap;\r\n break;\r\n }\r\n\r\n // Keep tooltip within viewport bounds\r\n if (left < gap) left = gap;\r\n if (left + tooltipRect.width > viewportWidth - gap) {\r\n left = viewportWidth - tooltipRect.width - gap;\r\n }\r\n if (top < gap) top = gap;\r\n if (top + tooltipRect.height > viewportHeight - gap) {\r\n top = viewportHeight - tooltipRect.height - gap;\r\n }\r\n\r\n setPosition({ top, left });\r\n setPositioned(true);\r\n }, [placement]);\r\n\r\n // Use useLayoutEffect for initial positioning - runs synchronously\r\n // before browser paint, preventing flash at wrong position\r\n useLayoutEffect(() => {\r\n if (visible && triggerRef.current && tooltipRef.current) {\r\n calculatePosition();\r\n }\r\n }, [visible, calculatePosition]);\r\n\r\n // Use useEffect for scroll/resize listeners\r\n useEffect(() => {\r\n if (!visible) return;\r\n\r\n const handleScrollOrResize = () => {\r\n calculatePosition();\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.addEventListener(\"resize\", handleScrollOrResize);\r\n\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.removeEventListener(\"resize\", handleScrollOrResize);\r\n };\r\n }, [visible, calculatePosition]);\r\n\r\n // Escape key dismisses tooltip (WCAG 1.4.13 Content on Hover or Focus)\r\n useEffect(() => {\r\n if (!visible) return;\r\n const handleEscape = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n hideImmediately();\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleEscape);\r\n return () => document.removeEventListener(\"keydown\", handleEscape);\r\n }, [visible, hideImmediately]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n };\r\n }, []);\r\n\r\n /**\r\n * Composed rather than replaced: a child given these already is a child\r\n * that does something with them, and taking that away to add a tooltip\r\n * would be a surprising thing for a tooltip to do.\r\n */\r\n const compose =\r\n (own: () => void, theirs?: (e: React.SyntheticEvent) => void) =>\r\n (e: React.SyntheticEvent) => {\r\n theirs?.(e);\r\n own();\r\n };\r\n\r\n const childProps = children.props as Record<string, never> & {\r\n onMouseEnter?: (e: React.SyntheticEvent) => void;\r\n onMouseLeave?: (e: React.SyntheticEvent) => void;\r\n onFocus?: (e: React.SyntheticEvent) => void;\r\n onBlur?: (e: React.SyntheticEvent) => void;\r\n };\r\n\r\n const trigger = asChild ? (\r\n React.cloneElement(children, {\r\n ref: triggerRef,\r\n onMouseEnter: compose(showTooltip, childProps.onMouseEnter),\r\n onMouseLeave: compose(hideTooltip, childProps.onMouseLeave),\r\n onFocus: compose(showTooltip, childProps.onFocus),\r\n onBlur: compose(hideTooltip, childProps.onBlur),\r\n } as React.Attributes)\r\n ) : (\r\n <div\r\n ref={triggerRef as React.RefObject<HTMLDivElement>}\r\n className={classNames(\"zendir-tooltip-trigger\", className)}\r\n style={{\r\n display: \"inline-flex\",\r\n pointerEvents: \"auto\",\r\n maxWidth: \"100%\",\r\n width: \"inherit\",\r\n }}\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n onFocus={showTooltip}\r\n onBlur={hideTooltip}\r\n >\r\n {children}\r\n </div>\r\n );\r\n\r\n return (\r\n <>\r\n {trigger}\r\n\r\n {visible &&\r\n ReactDOM.createPortal(\r\n <div\r\n ref={tooltipRef}\r\n role=\"tooltip\"\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n style={{\r\n position: \"fixed\",\r\n top: positioned ? position.top : 0,\r\n left: positioned ? position.left : 0,\r\n width: \"max-content\",\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n backgroundColor: tokens.colors.background.elevated,\r\n color: tokens.colors.text.primary,\r\n fontSize: \"0.75rem\",\r\n fontWeight: 500,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n borderRadius: tokens.borderRadius.md,\r\n border: `1px solid ${tokens.colors.accent.primary}20`,\r\n boxShadow: positioned\r\n ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10`\r\n : \"none\",\r\n zIndex: 2147483100,\r\n maxWidth: \"min(280px, calc(100vw - 24px))\",\r\n whiteSpace: \"normal\",\r\n pointerEvents: \"auto\",\r\n visibility: positioned ? \"visible\" : \"hidden\",\r\n opacity: positioned ? 1 : 0,\r\n transition: positioned ? undefined : \"none\",\r\n animation: positioned\r\n ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both`\r\n : \"none\",\r\n backdropFilter: \"blur(8px)\",\r\n }}\r\n >\r\n {content}\r\n <style>\r\n {`@keyframes zendir-tooltip { from { opacity: 0; transform: scale(0.95) translateY(${placement === \"bottom\" ? \"-4px\" : placement === \"top\" ? \"4px\" : \"0\"}); } to { opacity: 1; transform: scale(1) translateY(0); } }\r\n @media (prefers-reduced-motion: reduce) { @keyframes zendir-tooltip { from, to { opacity: 1; transform: none; } } }`}\r\n </style>\r\n </div>,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n});\r\n\r\nexport default Tooltip;\r\n"],"mappings":"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"}
|