@hyddenlabs/hydn-ui 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/data-grid/data-grid.d.ts.map +1 -1
- package/dist/components/data-display/data-grid/data-grid.js +2 -14
- package/dist/components/data-display/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-display/status-indicator/index.d.ts +3 -0
- package/dist/components/data-display/status-indicator/index.d.ts.map +1 -0
- package/dist/components/data-display/status-indicator/status-indicator.d.ts +23 -0
- package/dist/components/data-display/status-indicator/status-indicator.d.ts.map +1 -0
- package/dist/components/data-display/status-indicator/status-indicator.js +31 -0
- package/dist/components/data-display/status-indicator/status-indicator.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/navigation/dropdown/dropdown.js +1 -1
- package/dist/components/navigation/dropdown/dropdown.js.map +1 -1
- package/dist/components/navigation/navbar/navbar.js +1 -1
- package/dist/components/navigation/navbar/navbar.js.map +1 -1
- package/dist/components/typography/code/code.d.ts +5 -1
- package/dist/components/typography/code/code.d.ts.map +1 -1
- package/dist/components/typography/code/code.js +67 -4
- package/dist/components/typography/code/code.js.map +1 -1
- package/dist/components/typography/text/text.d.ts +6 -1
- package/dist/components/typography/text/text.d.ts.map +1 -1
- package/dist/components/typography/text/text.js +48 -3
- package/dist/components/typography/text/text.js.map +1 -1
- package/dist/index.js +26 -24
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.d.ts","sourceRoot":"","sources":["../../../../src/components/data-display/data-grid/data-grid.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA+B,MAAM,OAAO,CAAC;AAS/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAK7D,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"data-grid.d.ts","sourceRoot":"","sources":["../../../../src/components/data-display/data-grid/data-grid.tsx"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import Badge from "../badge/badge.js";
|
|
|
12
12
|
import Text from "../../typography/text/text.js";
|
|
13
13
|
import useTable from "../data-table/use-table.js";
|
|
14
14
|
import EmptyState from "../empty-state/empty-state.js";
|
|
15
|
+
import StatusIndicator from "../status-indicator/status-indicator.js";
|
|
15
16
|
import { Link } from "react-router-dom";
|
|
16
17
|
const INTERACTIVE_TAGS = /* @__PURE__ */ new Set(["A", "BUTTON", "INPUT", "SELECT", "TEXTAREA", "LABEL"]);
|
|
17
18
|
function isInteractiveElement(element, container) {
|
|
@@ -257,20 +258,7 @@ function DataGrid({
|
|
|
257
258
|
if (column.statusDotMap) {
|
|
258
259
|
const stringValue = String(value);
|
|
259
260
|
const dotConfig = column.statusDotMap[stringValue] || column.statusDotMap["*"] || { color: "neutral" };
|
|
260
|
-
|
|
261
|
-
const dotColorClasses = {
|
|
262
|
-
primary: "bg-primary",
|
|
263
|
-
accent: "bg-accent",
|
|
264
|
-
neutral: "bg-muted-foreground",
|
|
265
|
-
success: "bg-success",
|
|
266
|
-
warning: "bg-warning",
|
|
267
|
-
error: "bg-error",
|
|
268
|
-
info: "bg-info"
|
|
269
|
-
};
|
|
270
|
-
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 min-w-0", children: [
|
|
271
|
-
/* @__PURE__ */ jsx("span", { className: `size-2 rounded-full shrink-0 ${dotColorClasses[dotColor]}` }),
|
|
272
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: dotConfig.label || stringValue })
|
|
273
|
-
] });
|
|
261
|
+
return /* @__PURE__ */ jsx(StatusIndicator, { variant: dotConfig.color || "neutral", children: dotConfig.label || stringValue });
|
|
274
262
|
}
|
|
275
263
|
if (column.renderAsBadges) {
|
|
276
264
|
if (Array.isArray(value)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.js","sources":["../../../../src/components/data-display/data-grid/data-grid.tsx"],"sourcesContent":["import { ReactNode, useState, useEffect, useRef } from 'react';\nimport { Icon } from '../../system/icon/icon';\nimport IconButton from '../../forms/button/icon-button';\nimport Input from '../../forms/input/input';\nimport InputGroup from '../../forms/input-group/input-group';\n\nimport Checkbox from '../../forms/checkbox/checkbox';\nimport Button from '../../forms/button/button';\nimport Tooltip from '../../feedback/tooltip/tooltip';\nimport type { ButtonProps } from '../../forms/button/button';\nimport Stack from '../../layout/stack/stack';\nimport Badge from '../badge/badge';\nimport Text from '../../typography/text/text';\nimport { useTable, UseTableOptions } from '../data-table/use-table';\nimport { Size } from '@/theme/size-tokens';\nimport { ColorVariant } from '@/theme/tokens';\nimport EmptyState from '../empty-state/empty-state';\nimport { Link } from 'react-router-dom';\n\n/** Interactive element tags that should not trigger onRowClick callback */\nconst INTERACTIVE_TAGS = new Set(['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'LABEL']);\n\n/** Check if an element or any of its ancestors (up to container) is interactive - used for onRowClick only */\nfunction isInteractiveElement(element: HTMLElement | null, container: HTMLElement | null): boolean {\n let current = element;\n while (current && current !== container) {\n if (INTERACTIVE_TAGS.has(current.tagName)) return true;\n if (current.getAttribute('role') === 'button') return true;\n current = current.parentElement;\n }\n return false;\n}\n\nexport type DataGridAction<T> = {\n /** Icon name for the action button */\n icon: string;\n /** Size of the icon */\n iconSize?: Size;\n /** Color variant for the icon */\n iconColor?: ColorVariant;\n /** Icon to display on hover (optional) */\n hoverIcon?: string;\n /** Accessible label for the action (shown in tooltip or screen readers) */\n label: string;\n /** Tooltip text to display on hover. If not provided, label will be used for aria-label only. */\n tooltip?: string;\n /** Callback when the action is clicked */\n onClick: (row: T, index: number) => void;\n /** Visual variant for the action button */\n variant?: 'primary' | 'accent' | 'neutral' | 'success' | 'warning' | 'error' | 'info';\n};\n\nexport type DataGridActionItem<T> = DataGridAction<T> | ReactNode;\n\nexport type DataGridColumnDef<T> = {\n /** Key of the data property to display in this column */\n key: keyof T;\n /** Column header label */\n label: string;\n /** Whether this column can be sorted */\n sortable?: boolean;\n /** CSS width class or value for the column (e.g., 'w-48', '200px', 'flex-1') */\n width?: string;\n /**\n * Minimum width in pixels for the column. When set:\n * - Column truncates but never shrinks below this width\n * - Column auto-hides if container can't fit it at this width\n * - No need to also set hidePriority (though you can for explicit hide ordering)\n *\n * @example minWidth: 120 // Hide if email would be squished below 120px\n */\n minWidth?: number;\n /**\n * Maximum width in pixels for the column. Prevents column from growing too wide.\n * Use for columns like status/role that don't need much space.\n *\n * @example maxWidth: 100 // Cap role column at 100px\n */\n maxWidth?: number;\n /** Text alignment for cells in this column */\n align?: 'left' | 'center' | 'right';\n /** Whether text should wrap instead of truncating (useful for long strings) */\n wrapText?: boolean;\n /** Optional priority for responsive hiding. Lower values hide first when container width shrinks. Columns without hidePriority or minWidth are never hidden. */\n hidePriority?: number;\n /** Whether to show this column's label in mobile/vertical layout (default: true) */\n showLabelOnMobile?: boolean;\n /** Custom render function for cell content (use as escape hatch when config options don't cover your use case) */\n render?: (value: T[keyof T], row: T, index: number) => ReactNode;\n\n // Config-driven rendering options (preferred over custom render)\n /** Pre-built formatter for common data types */\n format?: 'date' | 'currency' | 'number' | 'percent';\n /** Options for Intl formatters (NumberFormat or DateTimeFormat) */\n formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions;\n /** Map values to badge variants. Unmatched values render with 'neutral' variant. Use '*' key to override the default fallback. */\n badgeMap?: {\n [key: string]: { variant?: ColorVariant; label?: string };\n };\n /** Map values to status dot colors. Renders text with a colored dot to the left. Lighter alternative to badges for status indicators. */\n statusDotMap?: {\n [key: string]: { color?: ColorVariant; label?: string };\n };\n /**\n * Render cell values as badges. When the value is an array, each item is rendered\n * as an individual badge; for scalar values, the value is wrapped in a single badge.\n */\n renderAsBadges?: boolean;\n /** Variant for badge rendering */\n badgeVariant?: ColorVariant;\n /** Size for badge rendering */\n componentSize?: 'sm' | 'md' | 'lg';\n /** Fallback value to display when cell value is null, undefined, or empty string */\n fallback?: string;\n};\n\nexport type DataGridProps<T> = {\n /** Array of data objects to display in the grid */\n data: T[];\n /** Column definitions specifying how to render each column */\n columns: DataGridColumnDef<T>[];\n /** Additional CSS classes to apply */\n className?: string;\n\n // Styling options\n /** Whether to apply striped row styling (alternating background) */\n striped?: boolean;\n /** Whether rows have hover effects */\n hoverable?: boolean;\n /** Whether to use compact spacing for dense data */\n compact?: boolean;\n\n // Features\n /** Whether columns can be sorted (can be overridden per column) */\n sortable?: boolean;\n /** Whether to enable client-side pagination */\n paginated?: boolean;\n /** Number of rows per page when pagination is enabled */\n pageSize?: number;\n /** Whether to show checkboxes for row selection */\n selectable?: boolean;\n /** Whether to enable search/filter functionality */\n searchable?: boolean;\n /** Specific keys to search within. If not provided, searches all fields. */\n searchKeys?: (keyof T)[];\n /** Placeholder text for the search input */\n searchPlaceholder?: string;\n /** Callback when search query changes */\n onSearchChange?: (query: string) => void;\n\n // Actions - either array of action configs/components OR function returning actions/render\n /** Action buttons, function returning actions based on row data, or custom render function for the actions column */\n actions?: DataGridActionItem<T>[] | ((row: T, index: number) => DataGridActionItem<T>[] | ReactNode);\n /** Label for the actions column header */\n actionsLabel?: string;\n\n // Row link - makes entire row clickable for navigation\n /** Function returning href for row navigation. When provided, entire row becomes a link. */\n rowHref?: (row: T, index: number) => string | undefined;\n\n // Callbacks\n /** Callback when a row is clicked (if rowHref not provided) */\n onRowClick?: (row: T, index: number) => void;\n /** Callback when row selection changes (provides array of selected row indices) */\n onSelectionChange?: (selectedIndices: number[]) => void;\n\n // Empty state\n emptyState?: {\n /** Message to display when there is no data */\n title?: string;\n /** Description to display when there is no data */\n description?: string;\n /** Text for the button in the empty state */\n buttonText?: string;\n /** Click handler for the button in the empty state */\n onButtonClick?: () => void;\n };\n\n // Initial state\n /** Initial sort configuration */\n initialSort?: { key: keyof T; direction: 'asc' | 'desc' };\n /** Title displayed above the grid (left side of header) */\n title?: string;\n /** Header actions displayed above the grid (right side of header). */\n headerActions?: HeaderAction[];\n};\n\nexport type HeaderAction = {\n /** Button text; if omitted and `icon` provided, button will be icon-only */\n label?: string;\n /** Icon name to render inside the button (maps to library `Icon`) */\n icon?: string;\n /** Accessible label for screen readers. Required for icon-only buttons (when label is omitted). */\n ariaLabel?: string;\n /** Click handler invoked when the button is clicked */\n onClick?: () => void;\n /** Button variant (maps to Button `variant` prop) */\n variant?: ButtonProps['variant'];\n /** Button style (maps to Button `style` prop) */\n style?: ButtonProps['style'];\n /** Button size (maps to Button `size` prop) */\n size?: ButtonProps['size'];\n /** Additional className forwarded to the Button */\n className?: string;\n};\n\n/** Breakpoint width (in px) below which we switch to vertical/mobile layout */\nconst MOBILE_BREAKPOINT = 640;\n\n/**\n * DataGrid - Responsive data display component with Vercel-style layout\n *\n * Features:\n * - Generic typed data and columns (same API as DataTable)\n * - Responsive layout: horizontal rows on desktop, vertical card-like on mobile\n * - Clickable rows via rowHref with interactive elements taking priority\n * - Client-side sorting, pagination, selection, and search\n * - Custom cell rendering with DataGridCell for multi-element stacking\n *\n * @example\n * ```tsx\n * <DataGrid\n * data={deployments}\n * columns={[\n * {\n * key: 'name',\n * label: 'Deployment',\n * render: (value, row) => (\n * <DataGridCell>\n * <Text weight=\"medium\">{row.name}</Text>\n * <Text size=\"sm\" variant=\"muted\">{row.url}</Text>\n * </DataGridCell>\n * )\n * },\n * { key: 'status', label: 'Status', badgeMap: { ready: { variant: 'success' } } },\n * { key: 'createdAt', label: 'Created', format: 'date' }\n * ]}\n * rowHref={(row) => `/deployments/${row.id}`}\n * hoverable\n * />\n * ```\n */\nfunction DataGrid<T>({\n data,\n columns,\n className = '',\n striped = false,\n hoverable = true,\n compact = false,\n sortable = true,\n paginated = false,\n pageSize = 10,\n selectable = false,\n searchable = false,\n searchKeys,\n searchPlaceholder = 'Search...',\n onSearchChange,\n actions,\n actionsLabel = 'Actions',\n rowHref,\n onRowClick,\n onSelectionChange,\n emptyState = {\n title: 'No data available',\n description: undefined,\n buttonText: undefined,\n onButtonClick: undefined\n },\n headerActions,\n title,\n initialSort\n}: DataGridProps<T>) {\n // Search state\n const [searchQuery, setSearchQuery] = useState('');\n\n // Layout state (desktop vs mobile)\n const [isMobile, setIsMobile] = useState(false);\n\n const handleSearchChange = (value: string) => {\n setSearchQuery(value);\n if (onSearchChange) {\n onSearchChange(value);\n }\n };\n\n // Container ref for responsive behavior\n const containerRef = useRef<HTMLDivElement>(null);\n const [visibleColumns, setVisibleColumns] = useState<DataGridColumnDef<T>[]>(() => columns);\n\n // Calculate minimum width needed for actions column based on max actions in any row\n const calculateActionsMinWidth = (): number => {\n if (!actions) return 0;\n\n let maxActionCount = 0;\n\n if (Array.isArray(actions)) {\n // Static actions - all rows have the same actions\n maxActionCount = actions.length;\n } else {\n // Dynamic actions - need to check all rows\n data.forEach((row, index) => {\n const result = actions(row, index);\n if (Array.isArray(result)) {\n maxActionCount = Math.max(maxActionCount, result.length);\n } else {\n // Custom render - assume it needs reasonable space (equivalent to 2 actions)\n maxActionCount = Math.max(maxActionCount, 2);\n }\n });\n }\n\n if (maxActionCount === 0) {\n // No actions, but we still need some minimum space in case actions appear\n return 60;\n }\n\n // Each action button takes ~36px (icon + button padding)\n // Stack spacing between actions is 8px (spacing=\"sm\")\n // Formula: (actionWidth * count) + (gap * (count - 1)) + extraPadding\n const actionWidth = 24;\n const gapWidth = 8;\n const extraPadding = 16; // breathing room\n\n return actionWidth * maxActionCount + gapWidth * Math.max(0, maxActionCount - 1) + extraPadding;\n };\n\n const actionsMinWidth = calculateActionsMinWidth();\n\n // ResizeObserver to handle responsive column hiding and mobile layout\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n // Check if any columns have hidePriority or minWidth set (both make columns hideable)\n const hasHideableColumns = columns.some((col) => col.hidePriority !== undefined || col.minWidth !== undefined);\n\n const updateLayout = () => {\n const containerWidth = container.offsetWidth;\n\n // Check if we should switch to mobile layout\n const nowMobile = containerWidth < MOBILE_BREAKPOINT;\n setIsMobile(nowMobile);\n\n // In mobile/vertical layout, show all columns (they stack vertically)\n // Only apply responsive hiding in desktop/horizontal mode\n if (nowMobile || !hasHideableColumns) {\n setVisibleColumns(columns);\n return;\n }\n\n // Columns are hideable if they have hidePriority OR minWidth\n const isHideable = (col: DataGridColumnDef<T>) => col.hidePriority !== undefined || col.minWidth !== undefined;\n\n // Separate columns into hideable and non-hideable\n const hideableColumns = columns\n .map((col, index) => ({ col, originalIndex: index }))\n .filter(({ col }) => isHideable(col))\n .sort((a, b) => {\n // Sort by hidePriority if both have it (lower hides first)\n const aPriority = a.col.hidePriority;\n const bPriority = b.col.hidePriority;\n\n if (aPriority !== undefined && bPriority !== undefined) {\n if (aPriority !== bPriority) return aPriority - bPriority;\n }\n // Columns with explicit hidePriority hide before minWidth-only columns\n if (aPriority !== undefined && bPriority === undefined) return -1;\n if (aPriority === undefined && bPriority !== undefined) return 1;\n\n // For minWidth-only columns, larger minWidth hides first (needs more space)\n const aMin = a.col.minWidth ?? 0;\n const bMin = b.col.minWidth ?? 0;\n if (aMin !== bMin) return bMin - aMin;\n\n // Finally, later columns hide first (preserve leading columns)\n return b.originalIndex - a.originalIndex;\n });\n\n const nonHideableColumns = columns.filter((col) => !isHideable(col));\n\n // Estimate column width - use minWidth if set, otherwise parse width or default\n const estimateColumnWidth = (col: DataGridColumnDef<T>): number => {\n // Use minWidth if explicitly set (this is the minimum space the column needs)\n if (col.minWidth) return col.minWidth;\n\n if (col.width) {\n const match = col.width.match(/(\\d+)/);\n if (match) {\n const num = parseInt(match[1], 10);\n if (col.width.includes('px')) return num;\n return num * 4;\n }\n }\n return 150;\n };\n\n // Calculate horizontal padding (px-3 = 24px total, px-4 = 32px total)\n const horizontalPadding = compact ? 24 : 32;\n // Grid gap is 16px (gap-4) between each column\n const gridGap = 16;\n\n // Start with non-hideable columns\n let currentWidth = nonHideableColumns.reduce((sum, col) => sum + estimateColumnWidth(col), 0);\n let totalColumns = nonHideableColumns.length;\n\n // Account for selection and actions columns\n if (selectable) {\n currentWidth += 48;\n totalColumns += 1;\n }\n if (actions) {\n currentWidth += actionsMinWidth;\n totalColumns += 1;\n }\n\n // Add hideable columns until we run out of space\n const columnsToShow: DataGridColumnDef<T>[] = [...nonHideableColumns];\n\n for (const { col } of hideableColumns) {\n const colWidth = estimateColumnWidth(col);\n const newTotalColumns = totalColumns + 1;\n // Calculate total gaps needed with the new column (N columns = N-1 gaps)\n const totalGaps = Math.max(0, newTotalColumns - 1) * gridGap;\n const totalWidthNeeded = currentWidth + colWidth + totalGaps + horizontalPadding;\n\n if (totalWidthNeeded <= containerWidth) {\n columnsToShow.push(col);\n currentWidth += colWidth;\n totalColumns = newTotalColumns;\n }\n }\n\n // Restore original column order\n const orderedVisibleColumns = columns.filter((col) => columnsToShow.includes(col));\n setVisibleColumns(orderedVisibleColumns);\n };\n\n // Call once on mount\n updateLayout();\n\n // Set up observer\n const observer = new ResizeObserver(() => {\n updateLayout();\n });\n\n observer.observe(container);\n\n return () => {\n observer.disconnect();\n };\n }, [columns, selectable, actions, actionsMinWidth, compact, data]);\n\n // Use table hook for state management\n const effectiveSearchKeys = searchKeys || visibleColumns.map((col) => col.key);\n\n const tableOptions: UseTableOptions<T> = {\n data,\n initialSort,\n pageSize,\n searchQuery,\n searchKeys: effectiveSearchKeys\n };\n\n const {\n currentData,\n sortedData,\n filteredData,\n sortConfig,\n handleSort,\n currentPage,\n totalPages,\n nextPage,\n prevPage,\n canNextPage,\n canPrevPage,\n selectedRows,\n toggleRow,\n toggleAll,\n isRowSelected,\n isAllSelected\n } = useTable(tableOptions);\n\n // Display data (paginated uses currentData, non-paginated uses sortedData)\n const displayData = paginated ? currentData : sortedData;\n\n // Header title and actions\n const hasHeader = Boolean(\n (title && String(title).length > 0) ||\n (Array.isArray(headerActions) ? headerActions.length > 0 : headerActions) ||\n searchable\n );\n\n // Selection handlers\n const handleToggleRow = (index: number) => {\n toggleRow(index);\n if (onSelectionChange) {\n const newSelection = new Set(selectedRows);\n if (newSelection.has(index)) {\n newSelection.delete(index);\n } else {\n newSelection.add(index);\n }\n onSelectionChange(Array.from(newSelection));\n }\n };\n\n const handleToggleAll = () => {\n toggleAll();\n if (onSelectionChange) {\n if (isAllSelected) {\n onSelectionChange([]);\n } else {\n const startIndex = (currentPage - 1) * pageSize;\n const allIndices = displayData.map((_, idx) => startIndex + idx);\n onSelectionChange(allIndices);\n }\n }\n };\n\n // Format value based on column configuration\n const formatValue = (value: unknown, column: DataGridColumnDef<T>): string => {\n if (value == null) return '';\n\n switch (column.format) {\n case 'date': {\n const dateValue = value instanceof Date ? value : new Date(value as string | number);\n return new Intl.DateTimeFormat('en-US', column.formatOptions as Intl.DateTimeFormatOptions).format(dateValue);\n }\n\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n ...column.formatOptions\n } as Intl.NumberFormatOptions).format(Number(value));\n\n case 'number':\n return new Intl.NumberFormat('en-US', column.formatOptions as Intl.NumberFormatOptions).format(Number(value));\n\n case 'percent':\n return new Intl.NumberFormat('en-US', {\n style: 'percent',\n ...column.formatOptions\n } as Intl.NumberFormatOptions).format(Number(value));\n\n default:\n return String(value);\n }\n };\n\n const renderCellContent = (value: unknown, column: DataGridColumnDef<T>, row: T, rowIndex: number): ReactNode => {\n const isEmpty = value == null || value === '';\n\n if (isEmpty && column.fallback !== undefined) {\n return column.fallback;\n }\n\n if (column.render) {\n return column.render(value as T[keyof T], row, rowIndex);\n }\n\n if (column.badgeMap) {\n const stringValue = String(value);\n const badgeConfig = column.badgeMap[stringValue] || column.badgeMap['*'] || { variant: 'neutral' };\n\n return (\n <Badge variant={badgeConfig.variant || 'neutral'} size={column.componentSize || 'md'}>\n {badgeConfig.label || stringValue}\n </Badge>\n );\n }\n\n if (column.statusDotMap) {\n const stringValue = String(value);\n const dotConfig = column.statusDotMap[stringValue] || column.statusDotMap['*'] || { color: 'neutral' };\n const dotColor = dotConfig.color || 'neutral';\n\n // Map color variants to Tailwind background classes\n const dotColorClasses: Record<ColorVariant, string> = {\n primary: 'bg-primary',\n accent: 'bg-accent',\n neutral: 'bg-muted-foreground',\n success: 'bg-success',\n warning: 'bg-warning',\n error: 'bg-error',\n info: 'bg-info'\n };\n\n return (\n <span className=\"inline-flex items-center gap-2 min-w-0\">\n <span className={`size-2 rounded-full shrink-0 ${dotColorClasses[dotColor]}`} />\n <span className=\"truncate\">{dotConfig.label || stringValue}</span>\n </span>\n );\n }\n\n if (column.renderAsBadges) {\n if (Array.isArray(value)) {\n if (value.length === 0 && column.fallback !== undefined) {\n return column.fallback;\n }\n\n return (\n <Stack direction=\"horizontal\" spacing=\"xs\" wrap>\n {value.map((item, idx) => (\n <Badge key={idx} variant={column.badgeVariant || 'neutral'} size={column.componentSize || 'md'}>\n {String(item)}\n </Badge>\n ))}\n </Stack>\n );\n } else {\n return (\n <Badge variant={column.badgeVariant || 'neutral'} size={column.componentSize || 'md'}>\n {String(value)}\n </Badge>\n );\n }\n }\n\n if (column.format) {\n return formatValue(value, column);\n }\n\n return String(value ?? '');\n };\n\n // Render sort indicator\n const renderSortIcon = (columnKey: keyof T) => {\n if (!sortable) return null;\n\n const isSorted = sortConfig?.key === columnKey;\n\n if (!isSorted) {\n return <Icon name=\"selector\" size=\"xs\" color=\"neutral\" />;\n }\n\n if (sortConfig?.direction === 'asc') {\n return <Icon name=\"chevron-up\" size=\"xs\" color=\"primary\" />;\n }\n\n return <Icon name=\"chevron-down\" size=\"xs\" color=\"primary\" />;\n };\n\n // Render actions for a row\n const renderActions = (row: T, actualIndex: number) => {\n if (!actions) return null;\n\n let rowActions: DataGridActionItem<T>[] | ReactNode;\n\n if (Array.isArray(actions)) {\n rowActions = actions;\n } else {\n const result = actions(row, actualIndex);\n if (Array.isArray(result)) {\n rowActions = result;\n } else {\n return <div data-interactive>{result}</div>;\n }\n }\n\n return (\n <Stack direction=\"horizontal\" spacing=\"sm\" data-interactive>\n {(rowActions as DataGridActionItem<T>[]).map((action, actionIndex) => {\n if (action && typeof action === 'object' && 'onClick' in action) {\n const actionConfig = action as DataGridAction<T>;\n const button = (\n <IconButton\n key={actionIndex}\n icon={actionConfig.icon}\n iconSize={actionConfig.iconSize || 'md'}\n buttonStyle=\"ghost\"\n variant={actionConfig.variant || 'neutral'}\n iconColor={actionConfig.iconColor}\n hoverIcon={actionConfig.hoverIcon}\n ariaLabel={actionConfig.label}\n onClick={() => actionConfig.onClick(row, actualIndex)}\n noPadding\n />\n );\n\n return actionConfig.tooltip ? (\n <Tooltip key={actionIndex} content={actionConfig.tooltip}>\n {button}\n </Tooltip>\n ) : (\n button\n );\n } else {\n return (\n <div key={actionIndex} data-interactive>\n {action as ReactNode}\n </div>\n );\n }\n })}\n </Stack>\n );\n };\n\n // Row padding classes\n const rowPadding = compact ? 'py-2 px-3' : 'py-3 px-4';\n\n // Build grid template columns for desktop layout\n const buildGridTemplateColumns = () => {\n const parts: string[] = [];\n\n // Selection checkbox column - fixed width for consistency across rows\n if (selectable) {\n parts.push('24px');\n }\n\n // Data columns\n visibleColumns.forEach((col) => {\n if (col.width) {\n // Explicit width (Tailwind class won't work in grid-template-columns)\n // Try to extract pixel value or use minmax\n const widthMatch = col.width.match(/(\\d+)px/);\n if (widthMatch) {\n parts.push(`${widthMatch[1]}px`);\n } else {\n parts.push('1fr');\n }\n } else if (col.minWidth && col.maxWidth) {\n // Both min and max - constrained range\n parts.push(`minmax(${col.minWidth}px, ${col.maxWidth}px)`);\n } else if (col.minWidth) {\n // Min only - can grow but not shrink below\n parts.push(`minmax(${col.minWidth}px, 1fr)`);\n } else if (col.maxWidth) {\n // Max only - can shrink but not grow beyond\n parts.push(`minmax(auto, ${col.maxWidth}px)`);\n } else {\n parts.push('1fr');\n }\n });\n\n // Actions column - dynamically sized based on max actions across all rows\n // Allows growing to max-content to accommodate multiple action buttons\n if (actions) {\n parts.push(`minmax(${actionsMinWidth}px, max-content)`);\n }\n\n return parts.join(' ');\n };\n\n const gridTemplateColumns = !isMobile ? buildGridTemplateColumns() : undefined;\n\n // Render a single row\n const renderRow = (row: T, rowIndex: number) => {\n const actualIndex = paginated ? (currentPage - 1) * pageSize + rowIndex : rowIndex;\n const isSelected = isRowSelected(actualIndex);\n const href = rowHref?.(row, actualIndex);\n const hasHref = Boolean(href);\n const hasOnRowClick = Boolean(onRowClick);\n const isClickable = hasHref || hasOnRowClick;\n\n // Row styling - add relative for Link overlay positioning\n const rowClasses = [\n 'relative',\n 'border-b border-border/50',\n striped && rowIndex % 2 === 1 ? 'bg-muted/30' : '',\n hoverable && isClickable ? 'hover:bg-muted/50 transition-colors' : '',\n isSelected ? 'bg-primary/5' : ''\n ]\n .filter(Boolean)\n .join(' ');\n\n // Desktop layout: CSS Grid for consistent column widths\n // Mobile layout: vertical flex\n const layoutClasses = isMobile ? 'flex flex-col gap-3' : 'grid items-center gap-4';\n\n // Grid style for desktop layout\n const rowStyle = !isMobile && gridTemplateColumns ? { gridTemplateColumns } : undefined;\n\n // Props for onRowClick (when no href) - handlers are on the row div\n // Uses isInteractiveElement check to avoid triggering on links/buttons\n const rowClickProps =\n !hasHref && hasOnRowClick\n ? {\n onClick: (e: React.MouseEvent<HTMLDivElement>) => {\n // Don't trigger if clicked on an interactive element (links, buttons, etc.)\n if (isInteractiveElement(e.target as HTMLElement, e.currentTarget)) {\n return;\n }\n onRowClick?.(row, actualIndex);\n },\n onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onRowClick?.(row, actualIndex);\n }\n },\n role: 'button' as const,\n tabIndex: 0,\n className: 'cursor-pointer'\n }\n : {};\n\n return (\n <div\n key={actualIndex}\n className={`${layoutClasses} ${rowPadding} ${rowClasses} ${rowClickProps.className || ''}`}\n style={rowStyle}\n onClick={rowClickProps.onClick}\n onKeyDown={rowClickProps.onKeyDown}\n role={rowClickProps.role}\n tabIndex={rowClickProps.tabIndex}\n >\n {/* Row link overlay - positioned behind content */}\n {hasHref && href && (\n <Link to={href} className=\"absolute inset-0 z-1\" aria-label={`View details`} tabIndex={0} />\n )}\n\n {/* Selection checkbox - above link layer */}\n {selectable && (\n <div className={`relative z-10 ${isMobile ? '' : 'shrink-0'}`}>\n <Checkbox\n checked={isSelected}\n onChange={() => handleToggleRow(actualIndex)}\n ariaLabel={`Select row ${actualIndex + 1}`}\n />\n </div>\n )}\n\n {/* Data cells - pointer-events-none lets clicks pass through to link layer */}\n {/* Interactive elements inside (links, buttons) keep their default pointer-events */}\n {visibleColumns.map((column) => {\n const value = row[column.key];\n const content = renderCellContent(value, column, row, actualIndex);\n const showLabel = isMobile && column.showLabelOnMobile !== false;\n\n // Alignment\n const alignClass =\n column.align === 'center' ? 'text-center' : column.align === 'right' ? 'text-right' : 'text-left';\n\n // Only disable pointer events when we have a link overlay\n const pointerClass = hasHref\n ? 'pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto [&_input]:pointer-events-auto [&_select]:pointer-events-auto [&_textarea]:pointer-events-auto [&_label]:pointer-events-auto [&_[data-interactive]]:pointer-events-auto'\n : '';\n\n return (\n <div\n key={String(column.key)}\n className={`${alignClass} ${isMobile ? 'flex flex-col gap-0.5' : 'min-w-0 overflow-hidden'} ${pointerClass}`}\n >\n {showLabel && (\n <Text size=\"xs\" variant=\"muted\" className=\"uppercase tracking-wide\">\n {column.label}\n </Text>\n )}\n <div className={column.wrapText ? '' : 'truncate'}>{content}</div>\n </div>\n );\n })}\n\n {/* Actions - above link layer */}\n {actions && (\n <div className={`relative z-10 shrink-0 ${isMobile ? 'flex justify-end pt-1' : 'justify-self-end'}`}>\n {renderActions(row, actualIndex)}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={containerRef} className={`overflow-hidden ${className}`}>\n {/* Header with title, search, actions */}\n {hasHeader && (\n <div className=\"mb-3\">\n {searchable ? (\n <div className=\"flex flex-col gap-3\">\n {title && <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>}\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-3 sm:justify-between\">\n <div className=\"flex-1 sm:max-w-md\">\n <InputGroup prefix={<Icon name=\"search\" size=\"sm\" />} className=\"w-full\">\n <Input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearchChange(e.target.value)}\n placeholder={searchPlaceholder}\n />\n </InputGroup>\n </div>\n\n {Array.isArray(headerActions) && headerActions.length > 0 && (\n <div className=\"flex items-center gap-2 flex-wrap sm:flex-nowrap ml-auto\">\n {headerActions.map((act, idx) => (\n <Button\n key={idx}\n onClick={act.onClick}\n variant={act.variant}\n style={act.style}\n size={act.size}\n className={act.className}\n ariaLabel={act.ariaLabel || (!act.label && act.icon ? 'Action' : undefined)}\n >\n {act.icon ? <Icon name={act.icon} size=\"sm\" /> : null}\n {act.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div className=\"flex-1\">\n {title && <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>}\n </div>\n\n {Array.isArray(headerActions) && headerActions.length > 0 && (\n <div className=\"flex items-center gap-2 flex-wrap sm:flex-nowrap\">\n {headerActions.map((act, idx) => (\n <Button\n key={idx}\n onClick={act.onClick}\n variant={act.variant}\n style={act.style}\n size={act.size}\n className={act.className}\n ariaLabel={act.ariaLabel || (!act.label && act.icon ? 'Action' : undefined)}\n >\n {act.icon ? <Icon name={act.icon} size=\"sm\" /> : null}\n {act.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )}\n\n {/* Empty states */}\n {data.length === 0 ? (\n <div className=\"border border-border rounded-lg\">\n <EmptyState\n title={emptyState.title}\n description={emptyState.description}\n buttonText={emptyState.buttonText}\n onButtonClick={emptyState.onButtonClick}\n />\n </div>\n ) : filteredData.length === 0 && searchQuery.trim() ? (\n <div className=\"border border-border rounded-lg\">\n <EmptyState\n title=\"No results found\"\n description={`No items match \"${searchQuery}\". Try adjusting your search.`}\n />\n </div>\n ) : (\n <>\n {/* Column headers (desktop only) */}\n {!isMobile && (\n <div\n className={`grid items-center gap-4 ${rowPadding} border-b border-border bg-muted/30`}\n style={gridTemplateColumns ? { gridTemplateColumns } : undefined}\n >\n {selectable && (\n <div>\n <Checkbox checked={isAllSelected} onChange={handleToggleAll} ariaLabel=\"Select all rows\" />\n </div>\n )}\n\n {visibleColumns.map((column) => {\n const alignClass =\n column.align === 'center' ? 'text-center' : column.align === 'right' ? 'text-right' : 'text-left';\n\n return (\n <div key={String(column.key)} className={`min-w-0 ${alignClass}`}>\n {column.sortable !== false && sortable ? (\n <button\n onClick={() => handleSort(column.key)}\n className=\"flex items-center gap-1 hover:text-foreground transition-colors font-medium text-sm text-muted-foreground\"\n type=\"button\"\n >\n {column.label}\n {renderSortIcon(column.key)}\n </button>\n ) : (\n <Text size=\"sm\" variant=\"muted\" weight=\"medium\">\n {column.label}\n </Text>\n )}\n </div>\n );\n })}\n\n {actions && (\n <div className=\"justify-self-end\">\n <Text size=\"sm\" variant=\"muted\" weight=\"medium\">\n {actionsLabel}\n </Text>\n </div>\n )}\n </div>\n )}\n\n {/* Select all checkbox (mobile) */}\n {isMobile && selectable && (\n <div className={`flex items-center gap-2 ${rowPadding} border-b border-border`}>\n <Checkbox checked={isAllSelected} onChange={handleToggleAll} ariaLabel=\"Select all rows\" />\n <Text size=\"sm\" variant=\"muted\">\n Select all\n </Text>\n </div>\n )}\n\n {/* Rows */}\n <div className=\"divide-y divide-border/50\">{displayData.map((row, idx) => renderRow(row, idx))}</div>\n\n {/* Pagination controls */}\n {paginated && totalPages > 1 && (\n <div className=\"flex items-center justify-between px-4 py-3 border-t border-border\">\n <div className=\"text-sm text-muted-foreground\">\n Page {currentPage} of {totalPages} ({filteredData.length} {searchQuery.trim() ? 'filtered' : 'total'}{' '}\n rows\n {searchQuery.trim() && data.length !== filteredData.length ? ` of ${data.length}` : ''})\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" style=\"outline\" onClick={prevPage} disabled={!canPrevPage}>\n Previous\n </Button>\n <Button size=\"sm\" style=\"outline\" onClick={nextPage} disabled={!canNextPage}>\n Next\n </Button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nDataGrid.displayName = 'DataGrid';\n\nexport default DataGrid;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-grid.js","sources":["../../../../src/components/data-display/data-grid/data-grid.tsx"],"sourcesContent":["import { ReactNode, useState, useEffect, useRef } from 'react';\nimport { Icon } from '../../system/icon/icon';\nimport IconButton from '../../forms/button/icon-button';\nimport Input from '../../forms/input/input';\nimport InputGroup from '../../forms/input-group/input-group';\n\nimport Checkbox from '../../forms/checkbox/checkbox';\nimport Button from '../../forms/button/button';\nimport Tooltip from '../../feedback/tooltip/tooltip';\nimport type { ButtonProps } from '../../forms/button/button';\nimport Stack from '../../layout/stack/stack';\nimport Badge from '../badge/badge';\nimport Text from '../../typography/text/text';\nimport { useTable, UseTableOptions } from '../data-table/use-table';\nimport { Size } from '@/theme/size-tokens';\nimport { ColorVariant } from '@/theme/tokens';\nimport EmptyState from '../empty-state/empty-state';\nimport StatusIndicator from '../status-indicator/status-indicator';\nimport { Link } from 'react-router-dom';\n\n/** Interactive element tags that should not trigger onRowClick callback */\nconst INTERACTIVE_TAGS = new Set(['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'LABEL']);\n\n/** Check if an element or any of its ancestors (up to container) is interactive - used for onRowClick only */\nfunction isInteractiveElement(element: HTMLElement | null, container: HTMLElement | null): boolean {\n let current = element;\n while (current && current !== container) {\n if (INTERACTIVE_TAGS.has(current.tagName)) return true;\n if (current.getAttribute('role') === 'button') return true;\n current = current.parentElement;\n }\n return false;\n}\n\nexport type DataGridAction<T> = {\n /** Icon name for the action button */\n icon: string;\n /** Size of the icon */\n iconSize?: Size;\n /** Color variant for the icon */\n iconColor?: ColorVariant;\n /** Icon to display on hover (optional) */\n hoverIcon?: string;\n /** Accessible label for the action (shown in tooltip or screen readers) */\n label: string;\n /** Tooltip text to display on hover. If not provided, label will be used for aria-label only. */\n tooltip?: string;\n /** Callback when the action is clicked */\n onClick: (row: T, index: number) => void;\n /** Visual variant for the action button */\n variant?: 'primary' | 'accent' | 'neutral' | 'success' | 'warning' | 'error' | 'info';\n};\n\nexport type DataGridActionItem<T> = DataGridAction<T> | ReactNode;\n\nexport type DataGridColumnDef<T> = {\n /** Key of the data property to display in this column */\n key: keyof T;\n /** Column header label */\n label: string;\n /** Whether this column can be sorted */\n sortable?: boolean;\n /** CSS width class or value for the column (e.g., 'w-48', '200px', 'flex-1') */\n width?: string;\n /**\n * Minimum width in pixels for the column. When set:\n * - Column truncates but never shrinks below this width\n * - Column auto-hides if container can't fit it at this width\n * - No need to also set hidePriority (though you can for explicit hide ordering)\n *\n * @example minWidth: 120 // Hide if email would be squished below 120px\n */\n minWidth?: number;\n /**\n * Maximum width in pixels for the column. Prevents column from growing too wide.\n * Use for columns like status/role that don't need much space.\n *\n * @example maxWidth: 100 // Cap role column at 100px\n */\n maxWidth?: number;\n /** Text alignment for cells in this column */\n align?: 'left' | 'center' | 'right';\n /** Whether text should wrap instead of truncating (useful for long strings) */\n wrapText?: boolean;\n /** Optional priority for responsive hiding. Lower values hide first when container width shrinks. Columns without hidePriority or minWidth are never hidden. */\n hidePriority?: number;\n /** Whether to show this column's label in mobile/vertical layout (default: true) */\n showLabelOnMobile?: boolean;\n /** Custom render function for cell content (use as escape hatch when config options don't cover your use case) */\n render?: (value: T[keyof T], row: T, index: number) => ReactNode;\n\n // Config-driven rendering options (preferred over custom render)\n /** Pre-built formatter for common data types */\n format?: 'date' | 'currency' | 'number' | 'percent';\n /** Options for Intl formatters (NumberFormat or DateTimeFormat) */\n formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions;\n /** Map values to badge variants. Unmatched values render with 'neutral' variant. Use '*' key to override the default fallback. */\n badgeMap?: {\n [key: string]: { variant?: ColorVariant; label?: string };\n };\n /** Map values to status dot colors. Renders text with a colored dot to the left. Lighter alternative to badges for status indicators. */\n statusDotMap?: {\n [key: string]: { color?: ColorVariant; label?: string };\n };\n /**\n * Render cell values as badges. When the value is an array, each item is rendered\n * as an individual badge; for scalar values, the value is wrapped in a single badge.\n */\n renderAsBadges?: boolean;\n /** Variant for badge rendering */\n badgeVariant?: ColorVariant;\n /** Size for badge rendering */\n componentSize?: 'sm' | 'md' | 'lg';\n /** Fallback value to display when cell value is null, undefined, or empty string */\n fallback?: string;\n};\n\nexport type DataGridProps<T> = {\n /** Array of data objects to display in the grid */\n data: T[];\n /** Column definitions specifying how to render each column */\n columns: DataGridColumnDef<T>[];\n /** Additional CSS classes to apply */\n className?: string;\n\n // Styling options\n /** Whether to apply striped row styling (alternating background) */\n striped?: boolean;\n /** Whether rows have hover effects */\n hoverable?: boolean;\n /** Whether to use compact spacing for dense data */\n compact?: boolean;\n\n // Features\n /** Whether columns can be sorted (can be overridden per column) */\n sortable?: boolean;\n /** Whether to enable client-side pagination */\n paginated?: boolean;\n /** Number of rows per page when pagination is enabled */\n pageSize?: number;\n /** Whether to show checkboxes for row selection */\n selectable?: boolean;\n /** Whether to enable search/filter functionality */\n searchable?: boolean;\n /** Specific keys to search within. If not provided, searches all fields. */\n searchKeys?: (keyof T)[];\n /** Placeholder text for the search input */\n searchPlaceholder?: string;\n /** Callback when search query changes */\n onSearchChange?: (query: string) => void;\n\n // Actions - either array of action configs/components OR function returning actions/render\n /** Action buttons, function returning actions based on row data, or custom render function for the actions column */\n actions?: DataGridActionItem<T>[] | ((row: T, index: number) => DataGridActionItem<T>[] | ReactNode);\n /** Label for the actions column header */\n actionsLabel?: string;\n\n // Row link - makes entire row clickable for navigation\n /** Function returning href for row navigation. When provided, entire row becomes a link. */\n rowHref?: (row: T, index: number) => string | undefined;\n\n // Callbacks\n /** Callback when a row is clicked (if rowHref not provided) */\n onRowClick?: (row: T, index: number) => void;\n /** Callback when row selection changes (provides array of selected row indices) */\n onSelectionChange?: (selectedIndices: number[]) => void;\n\n // Empty state\n emptyState?: {\n /** Message to display when there is no data */\n title?: string;\n /** Description to display when there is no data */\n description?: string;\n /** Text for the button in the empty state */\n buttonText?: string;\n /** Click handler for the button in the empty state */\n onButtonClick?: () => void;\n };\n\n // Initial state\n /** Initial sort configuration */\n initialSort?: { key: keyof T; direction: 'asc' | 'desc' };\n /** Title displayed above the grid (left side of header) */\n title?: string;\n /** Header actions displayed above the grid (right side of header). */\n headerActions?: HeaderAction[];\n};\n\nexport type HeaderAction = {\n /** Button text; if omitted and `icon` provided, button will be icon-only */\n label?: string;\n /** Icon name to render inside the button (maps to library `Icon`) */\n icon?: string;\n /** Accessible label for screen readers. Required for icon-only buttons (when label is omitted). */\n ariaLabel?: string;\n /** Click handler invoked when the button is clicked */\n onClick?: () => void;\n /** Button variant (maps to Button `variant` prop) */\n variant?: ButtonProps['variant'];\n /** Button style (maps to Button `style` prop) */\n style?: ButtonProps['style'];\n /** Button size (maps to Button `size` prop) */\n size?: ButtonProps['size'];\n /** Additional className forwarded to the Button */\n className?: string;\n};\n\n/** Breakpoint width (in px) below which we switch to vertical/mobile layout */\nconst MOBILE_BREAKPOINT = 640;\n\n/**\n * DataGrid - Responsive data display component with Vercel-style layout\n *\n * Features:\n * - Generic typed data and columns (same API as DataTable)\n * - Responsive layout: horizontal rows on desktop, vertical card-like on mobile\n * - Clickable rows via rowHref with interactive elements taking priority\n * - Client-side sorting, pagination, selection, and search\n * - Custom cell rendering with DataGridCell for multi-element stacking\n *\n * @example\n * ```tsx\n * <DataGrid\n * data={deployments}\n * columns={[\n * {\n * key: 'name',\n * label: 'Deployment',\n * render: (value, row) => (\n * <DataGridCell>\n * <Text weight=\"medium\">{row.name}</Text>\n * <Text size=\"sm\" variant=\"muted\">{row.url}</Text>\n * </DataGridCell>\n * )\n * },\n * { key: 'status', label: 'Status', badgeMap: { ready: { variant: 'success' } } },\n * { key: 'createdAt', label: 'Created', format: 'date' }\n * ]}\n * rowHref={(row) => `/deployments/${row.id}`}\n * hoverable\n * />\n * ```\n */\nfunction DataGrid<T>({\n data,\n columns,\n className = '',\n striped = false,\n hoverable = true,\n compact = false,\n sortable = true,\n paginated = false,\n pageSize = 10,\n selectable = false,\n searchable = false,\n searchKeys,\n searchPlaceholder = 'Search...',\n onSearchChange,\n actions,\n actionsLabel = 'Actions',\n rowHref,\n onRowClick,\n onSelectionChange,\n emptyState = {\n title: 'No data available',\n description: undefined,\n buttonText: undefined,\n onButtonClick: undefined\n },\n headerActions,\n title,\n initialSort\n}: DataGridProps<T>) {\n // Search state\n const [searchQuery, setSearchQuery] = useState('');\n\n // Layout state (desktop vs mobile)\n const [isMobile, setIsMobile] = useState(false);\n\n const handleSearchChange = (value: string) => {\n setSearchQuery(value);\n if (onSearchChange) {\n onSearchChange(value);\n }\n };\n\n // Container ref for responsive behavior\n const containerRef = useRef<HTMLDivElement>(null);\n const [visibleColumns, setVisibleColumns] = useState<DataGridColumnDef<T>[]>(() => columns);\n\n // Calculate minimum width needed for actions column based on max actions in any row\n const calculateActionsMinWidth = (): number => {\n if (!actions) return 0;\n\n let maxActionCount = 0;\n\n if (Array.isArray(actions)) {\n // Static actions - all rows have the same actions\n maxActionCount = actions.length;\n } else {\n // Dynamic actions - need to check all rows\n data.forEach((row, index) => {\n const result = actions(row, index);\n if (Array.isArray(result)) {\n maxActionCount = Math.max(maxActionCount, result.length);\n } else {\n // Custom render - assume it needs reasonable space (equivalent to 2 actions)\n maxActionCount = Math.max(maxActionCount, 2);\n }\n });\n }\n\n if (maxActionCount === 0) {\n // No actions, but we still need some minimum space in case actions appear\n return 60;\n }\n\n // Each action button takes ~36px (icon + button padding)\n // Stack spacing between actions is 8px (spacing=\"sm\")\n // Formula: (actionWidth * count) + (gap * (count - 1)) + extraPadding\n const actionWidth = 24;\n const gapWidth = 8;\n const extraPadding = 16; // breathing room\n\n return actionWidth * maxActionCount + gapWidth * Math.max(0, maxActionCount - 1) + extraPadding;\n };\n\n const actionsMinWidth = calculateActionsMinWidth();\n\n // ResizeObserver to handle responsive column hiding and mobile layout\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n // Check if any columns have hidePriority or minWidth set (both make columns hideable)\n const hasHideableColumns = columns.some((col) => col.hidePriority !== undefined || col.minWidth !== undefined);\n\n const updateLayout = () => {\n const containerWidth = container.offsetWidth;\n\n // Check if we should switch to mobile layout\n const nowMobile = containerWidth < MOBILE_BREAKPOINT;\n setIsMobile(nowMobile);\n\n // In mobile/vertical layout, show all columns (they stack vertically)\n // Only apply responsive hiding in desktop/horizontal mode\n if (nowMobile || !hasHideableColumns) {\n setVisibleColumns(columns);\n return;\n }\n\n // Columns are hideable if they have hidePriority OR minWidth\n const isHideable = (col: DataGridColumnDef<T>) => col.hidePriority !== undefined || col.minWidth !== undefined;\n\n // Separate columns into hideable and non-hideable\n const hideableColumns = columns\n .map((col, index) => ({ col, originalIndex: index }))\n .filter(({ col }) => isHideable(col))\n .sort((a, b) => {\n // Sort by hidePriority if both have it (lower hides first)\n const aPriority = a.col.hidePriority;\n const bPriority = b.col.hidePriority;\n\n if (aPriority !== undefined && bPriority !== undefined) {\n if (aPriority !== bPriority) return aPriority - bPriority;\n }\n // Columns with explicit hidePriority hide before minWidth-only columns\n if (aPriority !== undefined && bPriority === undefined) return -1;\n if (aPriority === undefined && bPriority !== undefined) return 1;\n\n // For minWidth-only columns, larger minWidth hides first (needs more space)\n const aMin = a.col.minWidth ?? 0;\n const bMin = b.col.minWidth ?? 0;\n if (aMin !== bMin) return bMin - aMin;\n\n // Finally, later columns hide first (preserve leading columns)\n return b.originalIndex - a.originalIndex;\n });\n\n const nonHideableColumns = columns.filter((col) => !isHideable(col));\n\n // Estimate column width - use minWidth if set, otherwise parse width or default\n const estimateColumnWidth = (col: DataGridColumnDef<T>): number => {\n // Use minWidth if explicitly set (this is the minimum space the column needs)\n if (col.minWidth) return col.minWidth;\n\n if (col.width) {\n const match = col.width.match(/(\\d+)/);\n if (match) {\n const num = parseInt(match[1], 10);\n if (col.width.includes('px')) return num;\n return num * 4;\n }\n }\n return 150;\n };\n\n // Calculate horizontal padding (px-3 = 24px total, px-4 = 32px total)\n const horizontalPadding = compact ? 24 : 32;\n // Grid gap is 16px (gap-4) between each column\n const gridGap = 16;\n\n // Start with non-hideable columns\n let currentWidth = nonHideableColumns.reduce((sum, col) => sum + estimateColumnWidth(col), 0);\n let totalColumns = nonHideableColumns.length;\n\n // Account for selection and actions columns\n if (selectable) {\n currentWidth += 48;\n totalColumns += 1;\n }\n if (actions) {\n currentWidth += actionsMinWidth;\n totalColumns += 1;\n }\n\n // Add hideable columns until we run out of space\n const columnsToShow: DataGridColumnDef<T>[] = [...nonHideableColumns];\n\n for (const { col } of hideableColumns) {\n const colWidth = estimateColumnWidth(col);\n const newTotalColumns = totalColumns + 1;\n // Calculate total gaps needed with the new column (N columns = N-1 gaps)\n const totalGaps = Math.max(0, newTotalColumns - 1) * gridGap;\n const totalWidthNeeded = currentWidth + colWidth + totalGaps + horizontalPadding;\n\n if (totalWidthNeeded <= containerWidth) {\n columnsToShow.push(col);\n currentWidth += colWidth;\n totalColumns = newTotalColumns;\n }\n }\n\n // Restore original column order\n const orderedVisibleColumns = columns.filter((col) => columnsToShow.includes(col));\n setVisibleColumns(orderedVisibleColumns);\n };\n\n // Call once on mount\n updateLayout();\n\n // Set up observer\n const observer = new ResizeObserver(() => {\n updateLayout();\n });\n\n observer.observe(container);\n\n return () => {\n observer.disconnect();\n };\n }, [columns, selectable, actions, actionsMinWidth, compact, data]);\n\n // Use table hook for state management\n const effectiveSearchKeys = searchKeys || visibleColumns.map((col) => col.key);\n\n const tableOptions: UseTableOptions<T> = {\n data,\n initialSort,\n pageSize,\n searchQuery,\n searchKeys: effectiveSearchKeys\n };\n\n const {\n currentData,\n sortedData,\n filteredData,\n sortConfig,\n handleSort,\n currentPage,\n totalPages,\n nextPage,\n prevPage,\n canNextPage,\n canPrevPage,\n selectedRows,\n toggleRow,\n toggleAll,\n isRowSelected,\n isAllSelected\n } = useTable(tableOptions);\n\n // Display data (paginated uses currentData, non-paginated uses sortedData)\n const displayData = paginated ? currentData : sortedData;\n\n // Header title and actions\n const hasHeader = Boolean(\n (title && String(title).length > 0) ||\n (Array.isArray(headerActions) ? headerActions.length > 0 : headerActions) ||\n searchable\n );\n\n // Selection handlers\n const handleToggleRow = (index: number) => {\n toggleRow(index);\n if (onSelectionChange) {\n const newSelection = new Set(selectedRows);\n if (newSelection.has(index)) {\n newSelection.delete(index);\n } else {\n newSelection.add(index);\n }\n onSelectionChange(Array.from(newSelection));\n }\n };\n\n const handleToggleAll = () => {\n toggleAll();\n if (onSelectionChange) {\n if (isAllSelected) {\n onSelectionChange([]);\n } else {\n const startIndex = (currentPage - 1) * pageSize;\n const allIndices = displayData.map((_, idx) => startIndex + idx);\n onSelectionChange(allIndices);\n }\n }\n };\n\n // Format value based on column configuration\n const formatValue = (value: unknown, column: DataGridColumnDef<T>): string => {\n if (value == null) return '';\n\n switch (column.format) {\n case 'date': {\n const dateValue = value instanceof Date ? value : new Date(value as string | number);\n return new Intl.DateTimeFormat('en-US', column.formatOptions as Intl.DateTimeFormatOptions).format(dateValue);\n }\n\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n ...column.formatOptions\n } as Intl.NumberFormatOptions).format(Number(value));\n\n case 'number':\n return new Intl.NumberFormat('en-US', column.formatOptions as Intl.NumberFormatOptions).format(Number(value));\n\n case 'percent':\n return new Intl.NumberFormat('en-US', {\n style: 'percent',\n ...column.formatOptions\n } as Intl.NumberFormatOptions).format(Number(value));\n\n default:\n return String(value);\n }\n };\n\n const renderCellContent = (value: unknown, column: DataGridColumnDef<T>, row: T, rowIndex: number): ReactNode => {\n const isEmpty = value == null || value === '';\n\n if (isEmpty && column.fallback !== undefined) {\n return column.fallback;\n }\n\n if (column.render) {\n return column.render(value as T[keyof T], row, rowIndex);\n }\n\n if (column.badgeMap) {\n const stringValue = String(value);\n const badgeConfig = column.badgeMap[stringValue] || column.badgeMap['*'] || { variant: 'neutral' };\n\n return (\n <Badge variant={badgeConfig.variant || 'neutral'} size={column.componentSize || 'md'}>\n {badgeConfig.label || stringValue}\n </Badge>\n );\n }\n\n if (column.statusDotMap) {\n const stringValue = String(value);\n const dotConfig = column.statusDotMap[stringValue] || column.statusDotMap['*'] || { color: 'neutral' };\n\n return <StatusIndicator variant={dotConfig.color || 'neutral'}>{dotConfig.label || stringValue}</StatusIndicator>;\n }\n\n if (column.renderAsBadges) {\n if (Array.isArray(value)) {\n if (value.length === 0 && column.fallback !== undefined) {\n return column.fallback;\n }\n\n return (\n <Stack direction=\"horizontal\" spacing=\"xs\" wrap>\n {value.map((item, idx) => (\n <Badge key={idx} variant={column.badgeVariant || 'neutral'} size={column.componentSize || 'md'}>\n {String(item)}\n </Badge>\n ))}\n </Stack>\n );\n } else {\n return (\n <Badge variant={column.badgeVariant || 'neutral'} size={column.componentSize || 'md'}>\n {String(value)}\n </Badge>\n );\n }\n }\n\n if (column.format) {\n return formatValue(value, column);\n }\n\n return String(value ?? '');\n };\n\n // Render sort indicator\n const renderSortIcon = (columnKey: keyof T) => {\n if (!sortable) return null;\n\n const isSorted = sortConfig?.key === columnKey;\n\n if (!isSorted) {\n return <Icon name=\"selector\" size=\"xs\" color=\"neutral\" />;\n }\n\n if (sortConfig?.direction === 'asc') {\n return <Icon name=\"chevron-up\" size=\"xs\" color=\"primary\" />;\n }\n\n return <Icon name=\"chevron-down\" size=\"xs\" color=\"primary\" />;\n };\n\n // Render actions for a row\n const renderActions = (row: T, actualIndex: number) => {\n if (!actions) return null;\n\n let rowActions: DataGridActionItem<T>[] | ReactNode;\n\n if (Array.isArray(actions)) {\n rowActions = actions;\n } else {\n const result = actions(row, actualIndex);\n if (Array.isArray(result)) {\n rowActions = result;\n } else {\n return <div data-interactive>{result}</div>;\n }\n }\n\n return (\n <Stack direction=\"horizontal\" spacing=\"sm\" data-interactive>\n {(rowActions as DataGridActionItem<T>[]).map((action, actionIndex) => {\n if (action && typeof action === 'object' && 'onClick' in action) {\n const actionConfig = action as DataGridAction<T>;\n const button = (\n <IconButton\n key={actionIndex}\n icon={actionConfig.icon}\n iconSize={actionConfig.iconSize || 'md'}\n buttonStyle=\"ghost\"\n variant={actionConfig.variant || 'neutral'}\n iconColor={actionConfig.iconColor}\n hoverIcon={actionConfig.hoverIcon}\n ariaLabel={actionConfig.label}\n onClick={() => actionConfig.onClick(row, actualIndex)}\n noPadding\n />\n );\n\n return actionConfig.tooltip ? (\n <Tooltip key={actionIndex} content={actionConfig.tooltip}>\n {button}\n </Tooltip>\n ) : (\n button\n );\n } else {\n return (\n <div key={actionIndex} data-interactive>\n {action as ReactNode}\n </div>\n );\n }\n })}\n </Stack>\n );\n };\n\n // Row padding classes\n const rowPadding = compact ? 'py-2 px-3' : 'py-3 px-4';\n\n // Build grid template columns for desktop layout\n const buildGridTemplateColumns = () => {\n const parts: string[] = [];\n\n // Selection checkbox column - fixed width for consistency across rows\n if (selectable) {\n parts.push('24px');\n }\n\n // Data columns\n visibleColumns.forEach((col) => {\n if (col.width) {\n // Explicit width (Tailwind class won't work in grid-template-columns)\n // Try to extract pixel value or use minmax\n const widthMatch = col.width.match(/(\\d+)px/);\n if (widthMatch) {\n parts.push(`${widthMatch[1]}px`);\n } else {\n parts.push('1fr');\n }\n } else if (col.minWidth && col.maxWidth) {\n // Both min and max - constrained range\n parts.push(`minmax(${col.minWidth}px, ${col.maxWidth}px)`);\n } else if (col.minWidth) {\n // Min only - can grow but not shrink below\n parts.push(`minmax(${col.minWidth}px, 1fr)`);\n } else if (col.maxWidth) {\n // Max only - can shrink but not grow beyond\n parts.push(`minmax(auto, ${col.maxWidth}px)`);\n } else {\n parts.push('1fr');\n }\n });\n\n // Actions column - dynamically sized based on max actions across all rows\n // Allows growing to max-content to accommodate multiple action buttons\n if (actions) {\n parts.push(`minmax(${actionsMinWidth}px, max-content)`);\n }\n\n return parts.join(' ');\n };\n\n const gridTemplateColumns = !isMobile ? buildGridTemplateColumns() : undefined;\n\n // Render a single row\n const renderRow = (row: T, rowIndex: number) => {\n const actualIndex = paginated ? (currentPage - 1) * pageSize + rowIndex : rowIndex;\n const isSelected = isRowSelected(actualIndex);\n const href = rowHref?.(row, actualIndex);\n const hasHref = Boolean(href);\n const hasOnRowClick = Boolean(onRowClick);\n const isClickable = hasHref || hasOnRowClick;\n\n // Row styling - add relative for Link overlay positioning\n const rowClasses = [\n 'relative',\n 'border-b border-border/50',\n striped && rowIndex % 2 === 1 ? 'bg-muted/30' : '',\n hoverable && isClickable ? 'hover:bg-muted/50 transition-colors' : '',\n isSelected ? 'bg-primary/5' : ''\n ]\n .filter(Boolean)\n .join(' ');\n\n // Desktop layout: CSS Grid for consistent column widths\n // Mobile layout: vertical flex\n const layoutClasses = isMobile ? 'flex flex-col gap-3' : 'grid items-center gap-4';\n\n // Grid style for desktop layout\n const rowStyle = !isMobile && gridTemplateColumns ? { gridTemplateColumns } : undefined;\n\n // Props for onRowClick (when no href) - handlers are on the row div\n // Uses isInteractiveElement check to avoid triggering on links/buttons\n const rowClickProps =\n !hasHref && hasOnRowClick\n ? {\n onClick: (e: React.MouseEvent<HTMLDivElement>) => {\n // Don't trigger if clicked on an interactive element (links, buttons, etc.)\n if (isInteractiveElement(e.target as HTMLElement, e.currentTarget)) {\n return;\n }\n onRowClick?.(row, actualIndex);\n },\n onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onRowClick?.(row, actualIndex);\n }\n },\n role: 'button' as const,\n tabIndex: 0,\n className: 'cursor-pointer'\n }\n : {};\n\n return (\n <div\n key={actualIndex}\n className={`${layoutClasses} ${rowPadding} ${rowClasses} ${rowClickProps.className || ''}`}\n style={rowStyle}\n onClick={rowClickProps.onClick}\n onKeyDown={rowClickProps.onKeyDown}\n role={rowClickProps.role}\n tabIndex={rowClickProps.tabIndex}\n >\n {/* Row link overlay - positioned behind content */}\n {hasHref && href && (\n <Link to={href} className=\"absolute inset-0 z-1\" aria-label={`View details`} tabIndex={0} />\n )}\n\n {/* Selection checkbox - above link layer */}\n {selectable && (\n <div className={`relative z-10 ${isMobile ? '' : 'shrink-0'}`}>\n <Checkbox\n checked={isSelected}\n onChange={() => handleToggleRow(actualIndex)}\n ariaLabel={`Select row ${actualIndex + 1}`}\n />\n </div>\n )}\n\n {/* Data cells - pointer-events-none lets clicks pass through to link layer */}\n {/* Interactive elements inside (links, buttons) keep their default pointer-events */}\n {visibleColumns.map((column) => {\n const value = row[column.key];\n const content = renderCellContent(value, column, row, actualIndex);\n const showLabel = isMobile && column.showLabelOnMobile !== false;\n\n // Alignment\n const alignClass =\n column.align === 'center' ? 'text-center' : column.align === 'right' ? 'text-right' : 'text-left';\n\n // Only disable pointer events when we have a link overlay\n const pointerClass = hasHref\n ? 'pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto [&_input]:pointer-events-auto [&_select]:pointer-events-auto [&_textarea]:pointer-events-auto [&_label]:pointer-events-auto [&_[data-interactive]]:pointer-events-auto'\n : '';\n\n return (\n <div\n key={String(column.key)}\n className={`${alignClass} ${isMobile ? 'flex flex-col gap-0.5' : 'min-w-0 overflow-hidden'} ${pointerClass}`}\n >\n {showLabel && (\n <Text size=\"xs\" variant=\"muted\" className=\"uppercase tracking-wide\">\n {column.label}\n </Text>\n )}\n <div className={column.wrapText ? '' : 'truncate'}>{content}</div>\n </div>\n );\n })}\n\n {/* Actions - above link layer */}\n {actions && (\n <div className={`relative z-10 shrink-0 ${isMobile ? 'flex justify-end pt-1' : 'justify-self-end'}`}>\n {renderActions(row, actualIndex)}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={containerRef} className={`overflow-hidden ${className}`}>\n {/* Header with title, search, actions */}\n {hasHeader && (\n <div className=\"mb-3\">\n {searchable ? (\n <div className=\"flex flex-col gap-3\">\n {title && <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>}\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-3 sm:justify-between\">\n <div className=\"flex-1 sm:max-w-md\">\n <InputGroup prefix={<Icon name=\"search\" size=\"sm\" />} className=\"w-full\">\n <Input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearchChange(e.target.value)}\n placeholder={searchPlaceholder}\n />\n </InputGroup>\n </div>\n\n {Array.isArray(headerActions) && headerActions.length > 0 && (\n <div className=\"flex items-center gap-2 flex-wrap sm:flex-nowrap ml-auto\">\n {headerActions.map((act, idx) => (\n <Button\n key={idx}\n onClick={act.onClick}\n variant={act.variant}\n style={act.style}\n size={act.size}\n className={act.className}\n ariaLabel={act.ariaLabel || (!act.label && act.icon ? 'Action' : undefined)}\n >\n {act.icon ? <Icon name={act.icon} size=\"sm\" /> : null}\n {act.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div className=\"flex-1\">\n {title && <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>}\n </div>\n\n {Array.isArray(headerActions) && headerActions.length > 0 && (\n <div className=\"flex items-center gap-2 flex-wrap sm:flex-nowrap\">\n {headerActions.map((act, idx) => (\n <Button\n key={idx}\n onClick={act.onClick}\n variant={act.variant}\n style={act.style}\n size={act.size}\n className={act.className}\n ariaLabel={act.ariaLabel || (!act.label && act.icon ? 'Action' : undefined)}\n >\n {act.icon ? <Icon name={act.icon} size=\"sm\" /> : null}\n {act.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )}\n\n {/* Empty states */}\n {data.length === 0 ? (\n <div className=\"border border-border rounded-lg\">\n <EmptyState\n title={emptyState.title}\n description={emptyState.description}\n buttonText={emptyState.buttonText}\n onButtonClick={emptyState.onButtonClick}\n />\n </div>\n ) : filteredData.length === 0 && searchQuery.trim() ? (\n <div className=\"border border-border rounded-lg\">\n <EmptyState\n title=\"No results found\"\n description={`No items match \"${searchQuery}\". Try adjusting your search.`}\n />\n </div>\n ) : (\n <>\n {/* Column headers (desktop only) */}\n {!isMobile && (\n <div\n className={`grid items-center gap-4 ${rowPadding} border-b border-border bg-muted/30`}\n style={gridTemplateColumns ? { gridTemplateColumns } : undefined}\n >\n {selectable && (\n <div>\n <Checkbox checked={isAllSelected} onChange={handleToggleAll} ariaLabel=\"Select all rows\" />\n </div>\n )}\n\n {visibleColumns.map((column) => {\n const alignClass =\n column.align === 'center' ? 'text-center' : column.align === 'right' ? 'text-right' : 'text-left';\n\n return (\n <div key={String(column.key)} className={`min-w-0 ${alignClass}`}>\n {column.sortable !== false && sortable ? (\n <button\n onClick={() => handleSort(column.key)}\n className=\"flex items-center gap-1 hover:text-foreground transition-colors font-medium text-sm text-muted-foreground\"\n type=\"button\"\n >\n {column.label}\n {renderSortIcon(column.key)}\n </button>\n ) : (\n <Text size=\"sm\" variant=\"muted\" weight=\"medium\">\n {column.label}\n </Text>\n )}\n </div>\n );\n })}\n\n {actions && (\n <div className=\"justify-self-end\">\n <Text size=\"sm\" variant=\"muted\" weight=\"medium\">\n {actionsLabel}\n </Text>\n </div>\n )}\n </div>\n )}\n\n {/* Select all checkbox (mobile) */}\n {isMobile && selectable && (\n <div className={`flex items-center gap-2 ${rowPadding} border-b border-border`}>\n <Checkbox checked={isAllSelected} onChange={handleToggleAll} ariaLabel=\"Select all rows\" />\n <Text size=\"sm\" variant=\"muted\">\n Select all\n </Text>\n </div>\n )}\n\n {/* Rows */}\n <div className=\"divide-y divide-border/50\">{displayData.map((row, idx) => renderRow(row, idx))}</div>\n\n {/* Pagination controls */}\n {paginated && totalPages > 1 && (\n <div className=\"flex items-center justify-between px-4 py-3 border-t border-border\">\n <div className=\"text-sm text-muted-foreground\">\n Page {currentPage} of {totalPages} ({filteredData.length} {searchQuery.trim() ? 'filtered' : 'total'}{' '}\n rows\n {searchQuery.trim() && data.length !== filteredData.length ? ` of ${data.length}` : ''})\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" style=\"outline\" onClick={prevPage} disabled={!canPrevPage}>\n Previous\n </Button>\n <Button size=\"sm\" style=\"outline\" onClick={nextPage} disabled={!canNextPage}>\n Next\n </Button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nDataGrid.displayName = 'DataGrid';\n\nexport default DataGrid;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/data-display/status-indicator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACzE,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ReactNode, HTMLAttributes } from 'react';
|
|
2
|
+
import { ColorVariant } from '../../../theme/tokens';
|
|
3
|
+
export type StatusIndicatorProps = HTMLAttributes<HTMLSpanElement> & {
|
|
4
|
+
/** The color variant for the status dot */
|
|
5
|
+
variant?: ColorVariant;
|
|
6
|
+
/** The text or content to display next to the indicator dot */
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
/** Optional CSS classes to apply to the container */
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Size of the status dot. Defaults to 'md' */
|
|
11
|
+
size?: 'sm' | 'md' | 'lg';
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* StatusIndicator displays a colored dot with text, commonly used for
|
|
15
|
+
* displaying status information like "Active", "Pending", "Inactive", etc.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <StatusIndicator variant="success">Active</StatusIndicator>
|
|
19
|
+
* <StatusIndicator variant="warning">Pending</StatusIndicator>
|
|
20
|
+
* <StatusIndicator variant="error">Inactive</StatusIndicator>
|
|
21
|
+
*/
|
|
22
|
+
export default function StatusIndicator({ variant, children, className, size, ...rest }: StatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
//# sourceMappingURL=status-indicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-indicator.d.ts","sourceRoot":"","sources":["../../../../src/components/data-display/status-indicator/status-indicator.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG;IACnE,2CAA2C;IAC3C,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,EAAE,SAAS,CAAC;IACpB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,OAAmB,EACnB,QAAQ,EACR,SAAc,EACd,IAAW,EACX,GAAG,IAAI,EACR,EAAE,oBAAoB,2CAyBtB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
function StatusIndicator({
|
|
3
|
+
variant = "neutral",
|
|
4
|
+
children,
|
|
5
|
+
className = "",
|
|
6
|
+
size = "md",
|
|
7
|
+
...rest
|
|
8
|
+
}) {
|
|
9
|
+
const dotColorClasses = {
|
|
10
|
+
primary: "bg-primary",
|
|
11
|
+
accent: "bg-accent",
|
|
12
|
+
neutral: "bg-muted-foreground",
|
|
13
|
+
success: "bg-success",
|
|
14
|
+
warning: "bg-warning",
|
|
15
|
+
error: "bg-error",
|
|
16
|
+
info: "bg-info"
|
|
17
|
+
};
|
|
18
|
+
const dotSizeClasses = {
|
|
19
|
+
sm: "size-1.5",
|
|
20
|
+
md: "size-2",
|
|
21
|
+
lg: "size-2.5"
|
|
22
|
+
};
|
|
23
|
+
return /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-2 min-w-0 ${className}`, ...rest, children: [
|
|
24
|
+
/* @__PURE__ */ jsx("span", { className: `${dotSizeClasses[size]} rounded-full shrink-0 ${dotColorClasses[variant]}` }),
|
|
25
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children })
|
|
26
|
+
] });
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
StatusIndicator as default
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=status-indicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-indicator.js","sources":["../../../../src/components/data-display/status-indicator/status-indicator.tsx"],"sourcesContent":["import { ReactNode, HTMLAttributes } from 'react';\nimport { ColorVariant } from '@/theme/tokens';\n\nexport type StatusIndicatorProps = HTMLAttributes<HTMLSpanElement> & {\n /** The color variant for the status dot */\n variant?: ColorVariant;\n /** The text or content to display next to the indicator dot */\n children: ReactNode;\n /** Optional CSS classes to apply to the container */\n className?: string;\n /** Size of the status dot. Defaults to 'md' */\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * StatusIndicator displays a colored dot with text, commonly used for\n * displaying status information like \"Active\", \"Pending\", \"Inactive\", etc.\n *\n * @example\n * <StatusIndicator variant=\"success\">Active</StatusIndicator>\n * <StatusIndicator variant=\"warning\">Pending</StatusIndicator>\n * <StatusIndicator variant=\"error\">Inactive</StatusIndicator>\n */\nexport default function StatusIndicator({\n variant = 'neutral',\n children,\n className = '',\n size = 'md',\n ...rest\n}: StatusIndicatorProps) {\n // Map color variants to Tailwind background classes\n const dotColorClasses: Record<ColorVariant, string> = {\n primary: 'bg-primary',\n accent: 'bg-accent',\n neutral: 'bg-muted-foreground',\n success: 'bg-success',\n warning: 'bg-warning',\n error: 'bg-error',\n info: 'bg-info'\n };\n\n // Map size to Tailwind size classes\n const dotSizeClasses = {\n sm: 'size-1.5',\n md: 'size-2',\n lg: 'size-2.5'\n };\n\n return (\n <span className={`inline-flex items-center gap-2 min-w-0 ${className}`} {...rest}>\n <span className={`${dotSizeClasses[size]} rounded-full shrink-0 ${dotColorClasses[variant]}`} />\n <span className=\"truncate\">{children}</span>\n </span>\n );\n}\n"],"names":[],"mappings":";AAuBA,SAAwB,gBAAgB;AAAA,EACtC,UAAU;AAAA,EACV;AAAA,EACA,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,GAAG;AACL,GAAyB;AAEvB,QAAM,kBAAgD;AAAA,IACpD,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,MAAM;AAAA,EAAA;AAIR,QAAM,iBAAiB;AAAA,IACrB,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAGN,8BACG,QAAA,EAAK,WAAW,0CAA0C,SAAS,IAAK,GAAG,MAC1E,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAW,GAAG,eAAe,IAAI,CAAC,0BAA0B,gBAAgB,OAAO,CAAC,GAAA,CAAI;AAAA,IAC9F,oBAAC,QAAA,EAAK,WAAU,YAAY,SAAA,CAAS;AAAA,EAAA,GACvC;AAEJ;"}
|
|
@@ -70,6 +70,8 @@ export type { DataGridProps, DataGridColumnDef, DataGridAction, DataGridActionIt
|
|
|
70
70
|
export type { DataGridCellProps } from './data-display/data-grid/data-grid-cell';
|
|
71
71
|
export { default as EmptyState } from './data-display/empty-state/empty-state';
|
|
72
72
|
export { default as List, ListItem } from './data-display/list/list';
|
|
73
|
+
export { default as StatusIndicator } from './data-display/status-indicator/status-indicator';
|
|
74
|
+
export type { StatusIndicatorProps } from './data-display/status-indicator/status-indicator';
|
|
73
75
|
export { default as Timeline, TimelineItem } from './data-display/timeline/timeline';
|
|
74
76
|
export { default as Table, TableHeader, TableBody, TableFooter, TableRow, TableHeadCell, TableCell } from './data-display/table/table';
|
|
75
77
|
export { default as Alert } from './feedback/alert/alert';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -175,7 +175,7 @@ function Dropdown({
|
|
|
175
175
|
"aria-haspopup": "true",
|
|
176
176
|
"aria-controls": isOpen ? "dropdown-menu" : void 0,
|
|
177
177
|
size: triggerSize,
|
|
178
|
-
className: `${noPadding ? "px-0" : ""} ${noBackgroundClass} ${triggerClassName}`,
|
|
178
|
+
className: `${noPadding ? "!px-0" : ""} ${noBackgroundClass} ${triggerClassName}`,
|
|
179
179
|
children: /* @__PURE__ */ jsxs("span", { className: `flex items-center ${gapSizes[effectiveGap]}`, children: [
|
|
180
180
|
iconNode && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: iconNode }),
|
|
181
181
|
(title || subtitle) && /* @__PURE__ */ jsxs("span", { className: subtitle ? "text-left" : "", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.js","sources":["../../../../src/components/navigation/dropdown/dropdown.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useCallback, createContext, useContext } from 'react';\nimport { createPortal } from 'react-dom';\nimport Button, { type ButtonProps } from '../../forms/button/button';\nimport { Icon } from '@/components/system/icon';\nimport { gapSizes, Size } from '@/theme/size-tokens';\n\nexport type DropdownTriggerProps = {\n /** Main label text */\n title: string;\n /** Optional secondary text shown below the title */\n subtitle?: string;\n /** Optional leading icon */\n icon?: string;\n /** Size of the leading icon */\n iconSize?: Size;\n /** Size variant for the trigger button */\n triggerSize?: ButtonProps['size'];\n /** Optional trailing icon */\n endIcon?: string;\n /** Remove horizontal padding from the trigger button */\n noPadding?: boolean;\n /** Additional className for the trigger button */\n className?: string;\n /** Gap between icon and text (defaults to iconSize if not specified). Uses mobile-first gapSizes (e.g., xs='gap-2 sm:gap-1', sm='gap-3 sm:gap-2'). */\n gap?: Size;\n};\n\nexport type DropdownProps = {\n /** Trigger configuration - rendered as an accessible Button */\n trigger: DropdownTriggerProps;\n children: React.ReactNode;\n className?: string;\n align?: 'start' | 'center' | 'end';\n autoClose?: boolean; // close on item select\n /** Size of the dropdown panel and items */\n size?: ButtonProps['size'];\n /** Button variant for the trigger */\n variant?: ButtonProps['variant'];\n /** Button style for the trigger ('none' removes all background colors) */\n buttonStyle?: ButtonProps['style'] | 'none';\n /** Minimum width for the dropdown menu and all items (e.g., '200px', '16rem') */\n minWidth?: string;\n /** Maximum width for the dropdown menu (e.g., '400px', '24rem') */\n maxWidth?: string;\n /** Additional styles for the dropdown menu */\n menuClassName?: string;\n};\n\ntype DropdownContextValue = {\n requestClose: () => void;\n autoClose: boolean;\n registerItem?: (el: HTMLButtonElement | null, index?: number) => void;\n minWidth?: string;\n size?: ButtonProps['size'];\n};\n\nconst DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown() {\n const ctx = useContext(DropdownContext);\n if (!ctx) throw new Error('useDropdown must be used within <Dropdown>');\n return ctx;\n}\n\n/**\n * Dropdown component with click-outside handling\n */\nfunction Dropdown({\n trigger,\n children,\n className = '',\n align = 'start',\n autoClose = true,\n size = 'md',\n variant = 'neutral',\n buttonStyle = 'solid',\n minWidth,\n maxWidth,\n menuClassName = ''\n}: Readonly<DropdownProps>) {\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const itemsRef = useRef<Array<HTMLButtonElement | null>>([]);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const [menuPosition, setMenuPosition] = useState<{ top: number; left: number; width: number } | null>(null);\n\n const close = useCallback(() => setIsOpen(false), []);\n const open = useCallback(() => setIsOpen(true), []);\n\n // Handle keyboard navigation\n useEffect(() => {\n if (!isOpen) return;\n const handleKey = (e: KeyboardEvent) => {\n if (!menuRef.current) return;\n const itemEls = itemsRef.current.filter(Boolean);\n if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) {\n e.preventDefault();\n if (itemEls.length === 0) return;\n let nextIndex = activeIndex;\n if (e.key === 'ArrowDown') nextIndex = (activeIndex + 1) % itemEls.length;\n if (e.key === 'ArrowUp') nextIndex = (activeIndex - 1 + itemEls.length) % itemEls.length;\n if (e.key === 'Home') nextIndex = 0;\n if (e.key === 'End') nextIndex = itemEls.length - 1;\n setActiveIndex(nextIndex);\n itemEls[nextIndex]?.focus();\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n triggerRef.current?.focus();\n }\n if (e.key === 'Tab') {\n // trap focus\n e.preventDefault();\n const itemEls = itemsRef.current.filter(Boolean);\n if (itemEls.length === 0) return;\n const dir = e.shiftKey ? -1 : 1;\n const nextIndex = (activeIndex + dir + itemEls.length) % itemEls.length;\n setActiveIndex(nextIndex);\n itemEls[nextIndex]?.focus();\n }\n };\n document.addEventListener('keydown', handleKey);\n return () => document.removeEventListener('keydown', handleKey);\n }, [isOpen, activeIndex, close]);\n\n // Outside click close\n useEffect(() => {\n if (!isOpen) return;\n const handleClickOutside = (event: MouseEvent) => {\n const isClickInTrigger = dropdownRef.current && dropdownRef.current.contains(event.target as Node);\n const isClickInMenu = menuRef.current && menuRef.current.contains(event.target as Node);\n\n if (!isClickInTrigger && !isClickInMenu) {\n close();\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [isOpen, close]);\n\n useLayoutEffect(() => {\n if (isOpen && triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect();\n setMenuPosition({\n top: rect.bottom,\n left: rect.left,\n width: rect.width\n });\n } else {\n setMenuPosition(null);\n }\n }, [isOpen]);\n\n // Close on scroll or resize (simpler than tracking position)\n useEffect(() => {\n if (!isOpen) return;\n\n const handleScrollOrResize = (event?: Event) => {\n // Allow scrolling inside the menu itself\n if (event && event.target && menuRef.current?.contains(event.target as Node)) {\n return;\n }\n close();\n };\n\n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n\n return () => {\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n };\n }, [isOpen, close]);\n\n // Auto-focus first item on open (useLayoutEffect to avoid cascading re-renders)\n useLayoutEffect(() => {\n let raf: number | undefined;\n if (isOpen) {\n const itemEls = itemsRef.current.filter(Boolean);\n if (itemEls.length) {\n // schedule the focus and state update to avoid cascading synchronous renders\n raf = requestAnimationFrame(() => {\n setActiveIndex(0);\n itemEls[0]?.focus();\n });\n }\n } else {\n raf = requestAnimationFrame(() => setActiveIndex(-1));\n }\n return () => {\n if (raf) cancelAnimationFrame(raf);\n };\n }, [isOpen]);\n\n const registerItem = useCallback((el: HTMLButtonElement | null, index?: number) => {\n // If index provided, set at index; otherwise push/remove based on presence\n if (typeof index === 'number' && index >= 0) {\n itemsRef.current[index] = el;\n return;\n }\n if (el === null) {\n // remove element references\n itemsRef.current = itemsRef.current.filter((x) => x !== el && x != null);\n return;\n }\n // Push new element at the end if not already present\n if (!itemsRef.current.includes(el)) itemsRef.current.push(el);\n }, []);\n\n const {\n title,\n subtitle,\n icon,\n iconSize = 'md',\n triggerSize = size,\n endIcon,\n noPadding = false,\n className: triggerClassName = '',\n gap\n } = trigger;\n\n const iconNode = icon ? <Icon name={icon} size={iconSize} color=\"currentColor\" /> : null;\n const endIconNode = endIcon ? <Icon name={endIcon} size={iconSize} color=\"currentColor\" /> : null;\n const effectiveGap = gap ?? iconSize;\n\n // Handle 'none' style by using ghost with bg removal\n const actualButtonStyle = buttonStyle === 'none' ? 'ghost' : buttonStyle;\n const noBackgroundClass =\n buttonStyle === 'none' ? '!bg-transparent hover:!bg-transparent active:!bg-transparent' : '';\n\n const getAlignmentStyle = (): React.CSSProperties => {\n if (!menuPosition) return {};\n const baseStyle: React.CSSProperties = {\n top: menuPosition.top + 4, // slightly lower than trigger\n minWidth: minWidth || undefined,\n maxWidth: maxWidth || undefined\n };\n\n if (align === 'start') {\n baseStyle.left = menuPosition.left;\n } else if (align === 'end') {\n baseStyle.right = window.innerWidth - (menuPosition.left + menuPosition.width);\n } else {\n baseStyle.left = menuPosition.left + menuPosition.width / 2;\n baseStyle.transform = 'translateX(-50%)';\n }\n\n return baseStyle;\n };\n\n return (\n <>\n <div ref={dropdownRef} className={`inline-block ${className}`}>\n <Button\n ref={triggerRef}\n variant={variant}\n style={actualButtonStyle}\n onClick={() => (isOpen ? close() : open())}\n aria-expanded={isOpen}\n aria-haspopup=\"true\"\n aria-controls={isOpen ? 'dropdown-menu' : undefined}\n size={triggerSize}\n className={`${noPadding ? 'px-0' : ''} ${noBackgroundClass} ${triggerClassName}`}\n >\n <span className={`flex items-center ${gapSizes[effectiveGap]}`}>\n {iconNode && <span className=\"shrink-0\">{iconNode}</span>}\n {(title || subtitle) && (\n <span className={subtitle ? 'text-left' : ''}>\n <span className={subtitle ? 'block font-semibold' : ''}>{title}</span>\n {subtitle && <span className=\"block text-xs text-muted-foreground font-normal\">{subtitle}</span>}\n </span>\n )}\n {endIconNode && <span className=\"shrink-0\">{endIconNode}</span>}\n </span>\n </Button>\n </div>\n {isOpen &&\n menuPosition &&\n createPortal(\n <DropdownContext.Provider value={{ requestClose: close, autoClose, registerItem, minWidth, size }}>\n <div\n id=\"dropdown-menu\"\n ref={menuRef}\n className={`fixed bg-popover text-popover-foreground border border-border rounded-md shadow-lg z-50 ${size === 'sm' ? 'min-w-36 text-xs py-1' : 'min-w-44'} max-h-80 overflow-y-auto origin-top animate-scaleIn focus:outline-none ${menuClassName}`}\n style={getAlignmentStyle()}\n role=\"menu\"\n aria-orientation=\"vertical\"\n tabIndex={-1}\n >\n {children}\n </div>\n </DropdownContext.Provider>,\n document.body\n )}\n </>\n );\n}\n\nDropdown.displayName = 'Dropdown';\n\nexport default Dropdown;\n\n// DropdownItem size mapping constants\n// Map size to text classes for main content\nconst sizeTextMap: Record<string, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-base',\n lg: 'text-lg',\n xl: 'text-xl'\n} as const;\n\n// Map size to description text classes (one step smaller)\nconst descriptionTextMap: Record<string, string> = {\n xs: 'text-[10px]', // smaller than xs\n sm: 'text-xs',\n md: 'text-sm',\n lg: 'text-base',\n xl: 'text-lg'\n} as const;\n\n// Map size to padding\nconst sizePaddingMap: Record<string, string> = {\n xs: 'px-2 py-1',\n sm: 'px-2.5 py-1.5',\n md: 'px-3.5 py-2',\n lg: 'px-4 py-2.5',\n xl: 'px-5 py-3'\n} as const;\n\n// DropdownItem component (simple abstraction)\nexport type DropdownItemProps = {\n children: React.ReactNode;\n onSelect?: () => void;\n /** Semantic color variant */\n variant?: 'neutral' | 'primary' | 'accent' | 'info' | 'success' | 'warning' | 'error';\n disabled?: boolean;\n className?: string;\n /** Optional muted description below main label */\n description?: string;\n /** Leading icon */\n icon?: string;\n iconSize?: Size;\n /** Trailing icon (e.g., checkmark for selected items) */\n endIcon?: string;\n /** Inherit size from dropdown or override */\n size?: 'sm' | 'md';\n /** Marks item as selected with enhanced background and optional checkmark */\n selected?: boolean;\n __dropdownIndex?: number; // injected\n __registerItem?: (el: HTMLButtonElement | null, index: number) => void; // injected (deprecated)\n};\n\nexport function DropdownItem({\n children,\n onSelect,\n variant = 'neutral',\n disabled = false,\n className = '',\n description,\n icon,\n iconSize,\n endIcon,\n size,\n selected = false\n}: Readonly<DropdownItemProps>) {\n const ctx = useContext(DropdownContext);\n const effectiveSize = size ?? ctx?.size ?? 'md';\n const effectiveIconSize = iconSize ?? effectiveSize;\n\n const base = `${sizeTextMap[effectiveSize] || sizeTextMap.md} ${sizePaddingMap[effectiveSize] || sizePaddingMap.md} w-full text-left select-none transition-colors outline-none disabled:opacity-40 disabled:pointer-events-none`;\n const focus =\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover';\n\n // Base palette for non-selected state\n const palette: Record<string, string> = {\n default: 'text-foreground/90 hover:bg-muted active:bg-muted/80',\n primary: 'text-primary hover:bg-primary/15 active:bg-primary/25 focus-visible:ring-primary',\n accent: 'text-accent hover:bg-accent/15 active:bg-accent/25 focus-visible:ring-accent',\n info: 'text-info hover:bg-info/15 active:bg-info/25 focus-visible:ring-info',\n success: 'text-success hover:bg-success/15 active:bg-success/25 focus-visible:ring-success',\n warning: 'text-warning hover:bg-warning/18 active:bg-warning/28 focus-visible:ring-warning',\n error: 'text-error hover:bg-error/15 active:bg-error/25 focus-visible:ring-error'\n };\n\n // Selected state palette - solid button style with foreground text\n const selectedPalette: Record<string, string> = {\n default: 'bg-muted text-foreground hover:bg-muted-hover hover:text-foreground',\n primary: 'bg-primary text-primary-foreground hover:bg-primary-hover hover:text-primary-foreground',\n accent: 'bg-accent text-accent-foreground hover:bg-accent-hover hover:text-accent-foreground',\n info: 'bg-info text-info-foreground hover:bg-info-hover hover:text-info-foreground',\n success: 'bg-success text-success-foreground hover:bg-success-hover hover:text-success-foreground',\n warning: 'bg-warning text-warning-foreground hover:bg-warning-hover hover:text-warning-foreground',\n error: 'bg-error text-error-foreground hover:bg-error-hover hover:text-error-foreground'\n };\n\n const variantClasses = selected\n ? selectedPalette[variant] || selectedPalette.default\n : palette[variant] || palette.default;\n\n // Determine layout: if we have icons, use horizontal layout, otherwise vertical\n const hasIcons = icon || endIcon;\n const iconNode = icon ? <Icon name={icon} size={effectiveIconSize} color=\"currentColor\" /> : null;\n const endIconNode = endIcon ? <Icon name={endIcon} size={effectiveIconSize} color=\"currentColor\" /> : null;\n\n return (\n <button\n ref={(el) => ctx?.registerItem?.(el)}\n role=\"menuitem\"\n type=\"button\"\n disabled={disabled}\n aria-current={selected ? 'true' : undefined}\n onClick={() => {\n if (disabled) return;\n onSelect?.();\n if (ctx?.autoClose) ctx.requestClose();\n }}\n className={`${base} ${focus} ${variantClasses} ${hasIcons ? 'flex flex-row items-center gap-2.5' : 'flex flex-col'} ${className} cursor-pointer`}\n >\n {iconNode && <span className=\"shrink-0\">{iconNode}</span>}\n <span className=\"flex-1 min-w-0\">\n <span className={`block font-medium leading-snug`}>{children}</span>\n {description && (\n <span\n className={`block ${descriptionTextMap[effectiveSize] || descriptionTextMap.md} text-current/80 mt-0.5 line-clamp-2`}\n >\n {description}\n </span>\n )}\n </span>\n {endIconNode && <span className=\"shrink-0\">{endIconNode}</span>}\n </button>\n );\n}\n\n/**\n * DropdownSeparator - Visual divider for grouping dropdown items\n * Use between items to create visual separation (e.g., separating error actions)\n */\nexport function DropdownSeparator({ className = '' }: Readonly<{ className?: string }>) {\n return <div role=\"separator\" className={`h-px bg-border ${className}`} />;\n}\n"],"names":["itemEls"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown.js","sources":["../../../../src/components/navigation/dropdown/dropdown.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useCallback, createContext, useContext } from 'react';\nimport { createPortal } from 'react-dom';\nimport Button, { type ButtonProps } from '../../forms/button/button';\nimport { Icon } from '@/components/system/icon';\nimport { gapSizes, Size } from '@/theme/size-tokens';\n\nexport type DropdownTriggerProps = {\n /** Main label text */\n title: string;\n /** Optional secondary text shown below the title */\n subtitle?: string;\n /** Optional leading icon */\n icon?: string;\n /** Size of the leading icon */\n iconSize?: Size;\n /** Size variant for the trigger button */\n triggerSize?: ButtonProps['size'];\n /** Optional trailing icon */\n endIcon?: string;\n /** Remove horizontal padding from the trigger button */\n noPadding?: boolean;\n /** Additional className for the trigger button */\n className?: string;\n /** Gap between icon and text (defaults to iconSize if not specified). Uses mobile-first gapSizes (e.g., xs='gap-2 sm:gap-1', sm='gap-3 sm:gap-2'). */\n gap?: Size;\n};\n\nexport type DropdownProps = {\n /** Trigger configuration - rendered as an accessible Button */\n trigger: DropdownTriggerProps;\n children: React.ReactNode;\n className?: string;\n align?: 'start' | 'center' | 'end';\n autoClose?: boolean; // close on item select\n /** Size of the dropdown panel and items */\n size?: ButtonProps['size'];\n /** Button variant for the trigger */\n variant?: ButtonProps['variant'];\n /** Button style for the trigger ('none' removes all background colors) */\n buttonStyle?: ButtonProps['style'] | 'none';\n /** Minimum width for the dropdown menu and all items (e.g., '200px', '16rem') */\n minWidth?: string;\n /** Maximum width for the dropdown menu (e.g., '400px', '24rem') */\n maxWidth?: string;\n /** Additional styles for the dropdown menu */\n menuClassName?: string;\n};\n\ntype DropdownContextValue = {\n requestClose: () => void;\n autoClose: boolean;\n registerItem?: (el: HTMLButtonElement | null, index?: number) => void;\n minWidth?: string;\n size?: ButtonProps['size'];\n};\n\nconst DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown() {\n const ctx = useContext(DropdownContext);\n if (!ctx) throw new Error('useDropdown must be used within <Dropdown>');\n return ctx;\n}\n\n/**\n * Dropdown component with click-outside handling\n */\nfunction Dropdown({\n trigger,\n children,\n className = '',\n align = 'start',\n autoClose = true,\n size = 'md',\n variant = 'neutral',\n buttonStyle = 'solid',\n minWidth,\n maxWidth,\n menuClassName = ''\n}: Readonly<DropdownProps>) {\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const itemsRef = useRef<Array<HTMLButtonElement | null>>([]);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const [menuPosition, setMenuPosition] = useState<{ top: number; left: number; width: number } | null>(null);\n\n const close = useCallback(() => setIsOpen(false), []);\n const open = useCallback(() => setIsOpen(true), []);\n\n // Handle keyboard navigation\n useEffect(() => {\n if (!isOpen) return;\n const handleKey = (e: KeyboardEvent) => {\n if (!menuRef.current) return;\n const itemEls = itemsRef.current.filter(Boolean);\n if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) {\n e.preventDefault();\n if (itemEls.length === 0) return;\n let nextIndex = activeIndex;\n if (e.key === 'ArrowDown') nextIndex = (activeIndex + 1) % itemEls.length;\n if (e.key === 'ArrowUp') nextIndex = (activeIndex - 1 + itemEls.length) % itemEls.length;\n if (e.key === 'Home') nextIndex = 0;\n if (e.key === 'End') nextIndex = itemEls.length - 1;\n setActiveIndex(nextIndex);\n itemEls[nextIndex]?.focus();\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n triggerRef.current?.focus();\n }\n if (e.key === 'Tab') {\n // trap focus\n e.preventDefault();\n const itemEls = itemsRef.current.filter(Boolean);\n if (itemEls.length === 0) return;\n const dir = e.shiftKey ? -1 : 1;\n const nextIndex = (activeIndex + dir + itemEls.length) % itemEls.length;\n setActiveIndex(nextIndex);\n itemEls[nextIndex]?.focus();\n }\n };\n document.addEventListener('keydown', handleKey);\n return () => document.removeEventListener('keydown', handleKey);\n }, [isOpen, activeIndex, close]);\n\n // Outside click close\n useEffect(() => {\n if (!isOpen) return;\n const handleClickOutside = (event: MouseEvent) => {\n const isClickInTrigger = dropdownRef.current && dropdownRef.current.contains(event.target as Node);\n const isClickInMenu = menuRef.current && menuRef.current.contains(event.target as Node);\n\n if (!isClickInTrigger && !isClickInMenu) {\n close();\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [isOpen, close]);\n\n useLayoutEffect(() => {\n if (isOpen && triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect();\n setMenuPosition({\n top: rect.bottom,\n left: rect.left,\n width: rect.width\n });\n } else {\n setMenuPosition(null);\n }\n }, [isOpen]);\n\n // Close on scroll or resize (simpler than tracking position)\n useEffect(() => {\n if (!isOpen) return;\n\n const handleScrollOrResize = (event?: Event) => {\n // Allow scrolling inside the menu itself\n if (event && event.target && menuRef.current?.contains(event.target as Node)) {\n return;\n }\n close();\n };\n\n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n\n return () => {\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n };\n }, [isOpen, close]);\n\n // Auto-focus first item on open (useLayoutEffect to avoid cascading re-renders)\n useLayoutEffect(() => {\n let raf: number | undefined;\n if (isOpen) {\n const itemEls = itemsRef.current.filter(Boolean);\n if (itemEls.length) {\n // schedule the focus and state update to avoid cascading synchronous renders\n raf = requestAnimationFrame(() => {\n setActiveIndex(0);\n itemEls[0]?.focus();\n });\n }\n } else {\n raf = requestAnimationFrame(() => setActiveIndex(-1));\n }\n return () => {\n if (raf) cancelAnimationFrame(raf);\n };\n }, [isOpen]);\n\n const registerItem = useCallback((el: HTMLButtonElement | null, index?: number) => {\n // If index provided, set at index; otherwise push/remove based on presence\n if (typeof index === 'number' && index >= 0) {\n itemsRef.current[index] = el;\n return;\n }\n if (el === null) {\n // remove element references\n itemsRef.current = itemsRef.current.filter((x) => x !== el && x != null);\n return;\n }\n // Push new element at the end if not already present\n if (!itemsRef.current.includes(el)) itemsRef.current.push(el);\n }, []);\n\n const {\n title,\n subtitle,\n icon,\n iconSize = 'md',\n triggerSize = size,\n endIcon,\n noPadding = false,\n className: triggerClassName = '',\n gap\n } = trigger;\n\n const iconNode = icon ? <Icon name={icon} size={iconSize} color=\"currentColor\" /> : null;\n const endIconNode = endIcon ? <Icon name={endIcon} size={iconSize} color=\"currentColor\" /> : null;\n const effectiveGap = gap ?? iconSize;\n\n // Handle 'none' style by using ghost with bg removal\n const actualButtonStyle = buttonStyle === 'none' ? 'ghost' : buttonStyle;\n const noBackgroundClass =\n buttonStyle === 'none' ? '!bg-transparent hover:!bg-transparent active:!bg-transparent' : '';\n\n const getAlignmentStyle = (): React.CSSProperties => {\n if (!menuPosition) return {};\n const baseStyle: React.CSSProperties = {\n top: menuPosition.top + 4, // slightly lower than trigger\n minWidth: minWidth || undefined,\n maxWidth: maxWidth || undefined\n };\n\n if (align === 'start') {\n baseStyle.left = menuPosition.left;\n } else if (align === 'end') {\n baseStyle.right = window.innerWidth - (menuPosition.left + menuPosition.width);\n } else {\n baseStyle.left = menuPosition.left + menuPosition.width / 2;\n baseStyle.transform = 'translateX(-50%)';\n }\n\n return baseStyle;\n };\n\n return (\n <>\n <div ref={dropdownRef} className={`inline-block ${className}`}>\n <Button\n ref={triggerRef}\n variant={variant}\n style={actualButtonStyle}\n onClick={() => (isOpen ? close() : open())}\n aria-expanded={isOpen}\n aria-haspopup=\"true\"\n aria-controls={isOpen ? 'dropdown-menu' : undefined}\n size={triggerSize}\n className={`${noPadding ? '!px-0' : ''} ${noBackgroundClass} ${triggerClassName}`}\n >\n <span className={`flex items-center ${gapSizes[effectiveGap]}`}>\n {iconNode && <span className=\"shrink-0\">{iconNode}</span>}\n {(title || subtitle) && (\n <span className={subtitle ? 'text-left' : ''}>\n <span className={subtitle ? 'block font-semibold' : ''}>{title}</span>\n {subtitle && <span className=\"block text-xs text-muted-foreground font-normal\">{subtitle}</span>}\n </span>\n )}\n {endIconNode && <span className=\"shrink-0\">{endIconNode}</span>}\n </span>\n </Button>\n </div>\n {isOpen &&\n menuPosition &&\n createPortal(\n <DropdownContext.Provider value={{ requestClose: close, autoClose, registerItem, minWidth, size }}>\n <div\n id=\"dropdown-menu\"\n ref={menuRef}\n className={`fixed bg-popover text-popover-foreground border border-border rounded-md shadow-lg z-50 ${size === 'sm' ? 'min-w-36 text-xs py-1' : 'min-w-44'} max-h-80 overflow-y-auto origin-top animate-scaleIn focus:outline-none ${menuClassName}`}\n style={getAlignmentStyle()}\n role=\"menu\"\n aria-orientation=\"vertical\"\n tabIndex={-1}\n >\n {children}\n </div>\n </DropdownContext.Provider>,\n document.body\n )}\n </>\n );\n}\n\nDropdown.displayName = 'Dropdown';\n\nexport default Dropdown;\n\n// DropdownItem size mapping constants\n// Map size to text classes for main content\nconst sizeTextMap: Record<string, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-base',\n lg: 'text-lg',\n xl: 'text-xl'\n} as const;\n\n// Map size to description text classes (one step smaller)\nconst descriptionTextMap: Record<string, string> = {\n xs: 'text-[10px]', // smaller than xs\n sm: 'text-xs',\n md: 'text-sm',\n lg: 'text-base',\n xl: 'text-lg'\n} as const;\n\n// Map size to padding\nconst sizePaddingMap: Record<string, string> = {\n xs: 'px-2 py-1',\n sm: 'px-2.5 py-1.5',\n md: 'px-3.5 py-2',\n lg: 'px-4 py-2.5',\n xl: 'px-5 py-3'\n} as const;\n\n// DropdownItem component (simple abstraction)\nexport type DropdownItemProps = {\n children: React.ReactNode;\n onSelect?: () => void;\n /** Semantic color variant */\n variant?: 'neutral' | 'primary' | 'accent' | 'info' | 'success' | 'warning' | 'error';\n disabled?: boolean;\n className?: string;\n /** Optional muted description below main label */\n description?: string;\n /** Leading icon */\n icon?: string;\n iconSize?: Size;\n /** Trailing icon (e.g., checkmark for selected items) */\n endIcon?: string;\n /** Inherit size from dropdown or override */\n size?: 'sm' | 'md';\n /** Marks item as selected with enhanced background and optional checkmark */\n selected?: boolean;\n __dropdownIndex?: number; // injected\n __registerItem?: (el: HTMLButtonElement | null, index: number) => void; // injected (deprecated)\n};\n\nexport function DropdownItem({\n children,\n onSelect,\n variant = 'neutral',\n disabled = false,\n className = '',\n description,\n icon,\n iconSize,\n endIcon,\n size,\n selected = false\n}: Readonly<DropdownItemProps>) {\n const ctx = useContext(DropdownContext);\n const effectiveSize = size ?? ctx?.size ?? 'md';\n const effectiveIconSize = iconSize ?? effectiveSize;\n\n const base = `${sizeTextMap[effectiveSize] || sizeTextMap.md} ${sizePaddingMap[effectiveSize] || sizePaddingMap.md} w-full text-left select-none transition-colors outline-none disabled:opacity-40 disabled:pointer-events-none`;\n const focus =\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover';\n\n // Base palette for non-selected state\n const palette: Record<string, string> = {\n default: 'text-foreground/90 hover:bg-muted active:bg-muted/80',\n primary: 'text-primary hover:bg-primary/15 active:bg-primary/25 focus-visible:ring-primary',\n accent: 'text-accent hover:bg-accent/15 active:bg-accent/25 focus-visible:ring-accent',\n info: 'text-info hover:bg-info/15 active:bg-info/25 focus-visible:ring-info',\n success: 'text-success hover:bg-success/15 active:bg-success/25 focus-visible:ring-success',\n warning: 'text-warning hover:bg-warning/18 active:bg-warning/28 focus-visible:ring-warning',\n error: 'text-error hover:bg-error/15 active:bg-error/25 focus-visible:ring-error'\n };\n\n // Selected state palette - solid button style with foreground text\n const selectedPalette: Record<string, string> = {\n default: 'bg-muted text-foreground hover:bg-muted-hover hover:text-foreground',\n primary: 'bg-primary text-primary-foreground hover:bg-primary-hover hover:text-primary-foreground',\n accent: 'bg-accent text-accent-foreground hover:bg-accent-hover hover:text-accent-foreground',\n info: 'bg-info text-info-foreground hover:bg-info-hover hover:text-info-foreground',\n success: 'bg-success text-success-foreground hover:bg-success-hover hover:text-success-foreground',\n warning: 'bg-warning text-warning-foreground hover:bg-warning-hover hover:text-warning-foreground',\n error: 'bg-error text-error-foreground hover:bg-error-hover hover:text-error-foreground'\n };\n\n const variantClasses = selected\n ? selectedPalette[variant] || selectedPalette.default\n : palette[variant] || palette.default;\n\n // Determine layout: if we have icons, use horizontal layout, otherwise vertical\n const hasIcons = icon || endIcon;\n const iconNode = icon ? <Icon name={icon} size={effectiveIconSize} color=\"currentColor\" /> : null;\n const endIconNode = endIcon ? <Icon name={endIcon} size={effectiveIconSize} color=\"currentColor\" /> : null;\n\n return (\n <button\n ref={(el) => ctx?.registerItem?.(el)}\n role=\"menuitem\"\n type=\"button\"\n disabled={disabled}\n aria-current={selected ? 'true' : undefined}\n onClick={() => {\n if (disabled) return;\n onSelect?.();\n if (ctx?.autoClose) ctx.requestClose();\n }}\n className={`${base} ${focus} ${variantClasses} ${hasIcons ? 'flex flex-row items-center gap-2.5' : 'flex flex-col'} ${className} cursor-pointer`}\n >\n {iconNode && <span className=\"shrink-0\">{iconNode}</span>}\n <span className=\"flex-1 min-w-0\">\n <span className={`block font-medium leading-snug`}>{children}</span>\n {description && (\n <span\n className={`block ${descriptionTextMap[effectiveSize] || descriptionTextMap.md} text-current/80 mt-0.5 line-clamp-2`}\n >\n {description}\n </span>\n )}\n </span>\n {endIconNode && <span className=\"shrink-0\">{endIconNode}</span>}\n </button>\n );\n}\n\n/**\n * DropdownSeparator - Visual divider for grouping dropdown items\n * Use between items to create visual separation (e.g., separating error actions)\n */\nexport function DropdownSeparator({ className = '' }: Readonly<{ className?: string }>) {\n return <div role=\"separator\" className={`h-px bg-border ${className}`} />;\n}\n"],"names":["itemEls"],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ function Navbar({
|
|
|
24
24
|
{
|
|
25
25
|
"data-appearance": appearance,
|
|
26
26
|
className: `${sticky ? "sticky top-0 z-50" : ""} ${appearanceClasses[appearance]} ${borderClasses[border]} ${className}`,
|
|
27
|
-
children: /* @__PURE__ */ jsx("div", { className: "w-auto max-w-full mx-
|
|
27
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-auto max-w-full mx-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between h-14 md:h-16 px-3 md:px-0", children: [
|
|
28
28
|
brand && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: brand }),
|
|
29
29
|
/* @__PURE__ */ jsx("div", { className: "hidden md:flex md:items-center md:space-x-6 md:flex-1 md:ml-10", children }),
|
|
30
30
|
actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: actions })
|