@fastnd/components 1.0.26 → 1.0.28

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.
Files changed (43) hide show
  1. package/dist/components/ColumnConfigPopover/ColumnConfigPopover.d.ts +20 -0
  2. package/dist/components/DoubleTextCell/DoubleTextCell.d.ts +9 -0
  3. package/dist/components/ProgressCircle/ProgressCircle.d.ts +9 -0
  4. package/dist/components/index.d.ts +3 -0
  5. package/dist/components/ui/badge.d.ts +1 -1
  6. package/dist/components/ui/button.d.ts +1 -1
  7. package/dist/components/ui/input-group.d.ts +1 -1
  8. package/dist/components/ui/item.d.ts +1 -1
  9. package/dist/components/ui/tabs.d.ts +1 -1
  10. package/dist/examples/dashboard/DashboardPage/DashboardPage.tsx +54 -0
  11. package/dist/examples/dashboard/ProjectList/ProjectList.tsx +120 -0
  12. package/dist/examples/dashboard/StatusDonutChart/StatusDonutChart.tsx +123 -0
  13. package/dist/examples/dashboard/StatusFilterLegend/StatusFilterLegend.tsx +70 -0
  14. package/dist/examples/dashboard/StatusOverview/StatusOverview.tsx +51 -0
  15. package/dist/examples/dashboard/constants.ts +18 -0
  16. package/dist/examples/dashboard/hooks/use-dashboard-state.ts +98 -0
  17. package/dist/examples/dashboard/index.ts +6 -0
  18. package/dist/examples/dashboard/types.ts +19 -0
  19. package/dist/examples/data-visualization/DataGrid/DataGrid.tsx +136 -0
  20. package/dist/examples/data-visualization/DataGridCardView/DataGridCardView.tsx +179 -0
  21. package/dist/examples/data-visualization/DataGridListView/DataGridListView.tsx +190 -0
  22. package/dist/examples/data-visualization/DataGridPage/DataGridPage.tsx +43 -0
  23. package/dist/examples/data-visualization/DataGridPagination/DataGridPagination.tsx +111 -0
  24. package/dist/examples/data-visualization/DataGridTableView/DataGridTableView.tsx +282 -0
  25. package/dist/examples/data-visualization/DataGridToolbar/DataGridToolbar.tsx +283 -0
  26. package/dist/examples/data-visualization/DomainSwitcher/DomainSwitcher.tsx +41 -0
  27. package/dist/examples/data-visualization/ExpansionDrawer/ExpansionDrawer.tsx +139 -0
  28. package/dist/examples/data-visualization/MoreFiltersPopover/MoreFiltersPopover.tsx +230 -0
  29. package/dist/examples/data-visualization/ResultCount/ResultCount.tsx +33 -0
  30. package/dist/examples/data-visualization/cell-renderers.tsx +119 -0
  31. package/dist/examples/data-visualization/constants.ts +1251 -0
  32. package/dist/examples/data-visualization/hooks/use-data-grid-columns.ts +65 -0
  33. package/dist/examples/data-visualization/hooks/use-data-grid-expansion.ts +40 -0
  34. package/dist/examples/data-visualization/hooks/use-data-grid-favorites.ts +41 -0
  35. package/dist/examples/data-visualization/hooks/use-data-grid-filters.ts +61 -0
  36. package/dist/examples/data-visualization/hooks/use-data-grid-pagination.ts +32 -0
  37. package/dist/examples/data-visualization/hooks/use-data-grid-sort.ts +32 -0
  38. package/dist/examples/data-visualization/hooks/use-data-grid-state.ts +133 -0
  39. package/dist/examples/data-visualization/hooks/use-filtered-data.ts +84 -0
  40. package/dist/examples/data-visualization/index.ts +10 -0
  41. package/dist/examples/data-visualization/types.ts +103 -0
  42. package/dist/fastnd-components.js +18759 -15519
  43. package/package.json +2 -1
@@ -0,0 +1,65 @@
1
+ import { useState, useMemo, useCallback } from 'react'
2
+ import type { ColumnConfig } from '../types'
3
+
4
+ export interface DataGridColumnsState {
5
+ visibleColumns: Set<string>
6
+ columnOrder: string[]
7
+ columnWidths: Record<string, number>
8
+ toggleColumnVisibility: (columnKey: string) => void
9
+ reorderColumns: (newOrder: string[]) => void
10
+ resizeColumn: (columnKey: string, width: number) => void
11
+ orderedVisibleColumns: Array<[string, ColumnConfig]>
12
+ }
13
+
14
+ export function useDataGridColumns(
15
+ columnConfig: Record<string, ColumnConfig>,
16
+ ): DataGridColumnsState {
17
+ const [visibleColumns, setVisibleColumns] = useState<Set<string>>(
18
+ () =>
19
+ new Set(
20
+ Object.entries(columnConfig)
21
+ .filter(([, config]) => config.visible)
22
+ .map(([key]) => key),
23
+ ),
24
+ )
25
+ const [columnOrder, setColumnOrder] = useState<string[]>(() => Object.keys(columnConfig))
26
+ const [columnWidths, setColumnWidths] = useState<Record<string, number>>({})
27
+
28
+ const toggleColumnVisibility = useCallback((columnKey: string) => {
29
+ setVisibleColumns((prev) => {
30
+ const next = new Set(prev)
31
+ if (next.has(columnKey)) {
32
+ next.delete(columnKey)
33
+ } else {
34
+ next.add(columnKey)
35
+ }
36
+ return next
37
+ })
38
+ }, [])
39
+
40
+ const reorderColumns = useCallback((newOrder: string[]) => {
41
+ setColumnOrder(newOrder)
42
+ }, [])
43
+
44
+ const resizeColumn = useCallback((columnKey: string, width: number) => {
45
+ setColumnWidths((prev) => ({ ...prev, [columnKey]: width }))
46
+ }, [])
47
+
48
+ const orderedVisibleColumns = useMemo<Array<[string, ColumnConfig]>>(
49
+ () =>
50
+ columnOrder
51
+ .filter((key) => visibleColumns.has(key) && key in columnConfig)
52
+ .map((key) => [key, columnConfig[key]]),
53
+ [columnOrder, visibleColumns, columnConfig],
54
+ )
55
+
56
+ return {
57
+ visibleColumns,
58
+ columnOrder,
59
+ columnWidths,
60
+ toggleColumnVisibility,
61
+ reorderColumns,
62
+ resizeColumn,
63
+ orderedVisibleColumns,
64
+ }
65
+ }
@@ -0,0 +1,40 @@
1
+ import { useState, useCallback } from 'react'
2
+
3
+ export interface DataGridExpansionState {
4
+ expandedRows: Set<string>
5
+ toggleExpand: (rowId: string, expandKey: string) => void
6
+ isExpanded: (rowId: string, expandKey: string) => boolean
7
+ collapseAll: () => void
8
+ }
9
+
10
+ function compositeKey(rowId: string, expandKey: string): string {
11
+ return `${rowId}::${expandKey}`
12
+ }
13
+
14
+ export function useDataGridExpansion(): DataGridExpansionState {
15
+ const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
16
+
17
+ const toggleExpand = useCallback((rowId: string, expandKey: string) => {
18
+ const key = compositeKey(rowId, expandKey)
19
+ setExpandedRows((prev) => {
20
+ const next = new Set(prev)
21
+ if (next.has(key)) {
22
+ next.delete(key)
23
+ } else {
24
+ next.add(key)
25
+ }
26
+ return next
27
+ })
28
+ }, [])
29
+
30
+ const isExpanded = useCallback(
31
+ (rowId: string, expandKey: string) => expandedRows.has(compositeKey(rowId, expandKey)),
32
+ [expandedRows],
33
+ )
34
+
35
+ const collapseAll = useCallback(() => {
36
+ setExpandedRows(new Set())
37
+ }, [])
38
+
39
+ return { expandedRows, toggleExpand, isExpanded, collapseAll }
40
+ }
@@ -0,0 +1,41 @@
1
+ import { useState, useCallback, useEffect } from 'react'
2
+ import type { DataRow } from '../types'
3
+
4
+ export interface DataGridFavoritesState {
5
+ favorites: Set<string>
6
+ toggleFavorite: (rowId: string) => void
7
+ isFavorite: (rowId: string) => boolean
8
+ }
9
+
10
+ function buildInitialFavorites(data: DataRow[]): Set<string> {
11
+ return new Set(data.filter((row) => row.favorite).map((row) => row.id))
12
+ }
13
+
14
+ export function useDataGridFavorites(initialData: DataRow[]): DataGridFavoritesState {
15
+ const [favorites, setFavorites] = useState<Set<string>>(() =>
16
+ buildInitialFavorites(initialData),
17
+ )
18
+
19
+ // Re-initialize when the dataset changes (e.g. domain switch via parent key remount).
20
+ // This useEffect handles cases where the same hook instance receives new data without
21
+ // remounting — keeps the hook correct in both key-based and prop-based reset scenarios.
22
+ useEffect(() => {
23
+ setFavorites(buildInitialFavorites(initialData))
24
+ }, [initialData])
25
+
26
+ const toggleFavorite = useCallback((rowId: string) => {
27
+ setFavorites((prev) => {
28
+ const next = new Set(prev)
29
+ if (next.has(rowId)) {
30
+ next.delete(rowId)
31
+ } else {
32
+ next.add(rowId)
33
+ }
34
+ return next
35
+ })
36
+ }, [])
37
+
38
+ const isFavorite = useCallback((rowId: string) => favorites.has(rowId), [favorites])
39
+
40
+ return { favorites, toggleFavorite, isFavorite }
41
+ }
@@ -0,0 +1,61 @@
1
+ import { useState, useMemo, useCallback } from 'react'
2
+ import type { ColumnConfig, DataGridFilters } from '../types'
3
+
4
+ export interface DataGridFiltersState {
5
+ filters: DataGridFilters
6
+ searchQuery: string
7
+ setFilter: (columnKey: string, values: string[] | null) => void
8
+ setSearchQuery: (query: string) => void
9
+ resetFilters: () => void
10
+ hasActiveFilters: boolean
11
+ activeFilterCount: number
12
+ }
13
+
14
+ function buildInitialFilters(columnConfig: Record<string, ColumnConfig>): DataGridFilters {
15
+ return Object.entries(columnConfig).reduce<DataGridFilters>((acc, [key, config]) => {
16
+ if (config.filterable) acc[key] = null
17
+ return acc
18
+ }, {})
19
+ }
20
+
21
+ export function useDataGridFilters(
22
+ columnConfig: Record<string, ColumnConfig>,
23
+ ): DataGridFiltersState {
24
+ const [filters, setFilters] = useState<DataGridFilters>(() =>
25
+ buildInitialFilters(columnConfig),
26
+ )
27
+ const [searchQuery, setSearchQuery] = useState('')
28
+
29
+ const setFilter = useCallback((columnKey: string, value: string | null) => {
30
+ setFilters((prev) => ({ ...prev, [columnKey]: value }))
31
+ }, [])
32
+
33
+ const handleSetSearchQuery = useCallback((query: string) => {
34
+ setSearchQuery(query)
35
+ }, [])
36
+
37
+ const resetFilters = useCallback(() => {
38
+ setFilters(buildInitialFilters(columnConfig))
39
+ setSearchQuery('')
40
+ }, [columnConfig])
41
+
42
+ const activeFilterCount = useMemo(
43
+ () => Object.values(filters).filter((v) => v !== null && v.length > 0).length,
44
+ [filters],
45
+ )
46
+
47
+ const hasActiveFilters = useMemo(
48
+ () => activeFilterCount > 0 || searchQuery !== '',
49
+ [activeFilterCount, searchQuery],
50
+ )
51
+
52
+ return {
53
+ filters,
54
+ searchQuery,
55
+ setFilter,
56
+ setSearchQuery: handleSetSearchQuery,
57
+ resetFilters,
58
+ hasActiveFilters,
59
+ activeFilterCount,
60
+ }
61
+ }
@@ -0,0 +1,32 @@
1
+ import { useState, useEffect } from 'react'
2
+ import type { DataRow } from '../types'
3
+
4
+ export type PageSize = 25 | 50 | 100
5
+
6
+ export interface PaginationState {
7
+ pageSize: PageSize
8
+ setPageSize: (size: PageSize) => void
9
+ currentPage: number
10
+ setCurrentPage: (page: number) => void
11
+ totalPages: number
12
+ pagedData: DataRow[]
13
+ }
14
+
15
+ export function useDataGridPagination(filteredData: DataRow[]): PaginationState {
16
+ const [pageSize, setPageSize] = useState<PageSize>(25)
17
+ const [currentPage, setCurrentPage] = useState(1)
18
+
19
+ // Reset to page 1 whenever the filtered result set changes size
20
+ useEffect(() => {
21
+ setCurrentPage(1)
22
+ }, [filteredData.length])
23
+
24
+ const totalPages = Math.max(1, Math.ceil(filteredData.length / pageSize))
25
+ const safeCurrentPage = Math.min(currentPage, totalPages)
26
+ const pagedData = filteredData.slice(
27
+ (safeCurrentPage - 1) * pageSize,
28
+ safeCurrentPage * pageSize,
29
+ )
30
+
31
+ return { pageSize, setPageSize, currentPage: safeCurrentPage, setCurrentPage, totalPages, pagedData }
32
+ }
@@ -0,0 +1,32 @@
1
+ import { useState, useCallback } from 'react'
2
+ import type { SortDirection } from '../types'
3
+
4
+ export interface DataGridSortState {
5
+ sortColumn: string | null
6
+ sortDirection: SortDirection
7
+ toggleSort: (columnKey: string) => void
8
+ resetSort: () => void
9
+ }
10
+
11
+ export function useDataGridSort(): DataGridSortState {
12
+ const [sortColumn, setSortColumn] = useState<string | null>(null)
13
+ const [sortDirection, setSortDirection] = useState<SortDirection>('asc')
14
+
15
+ const toggleSort = useCallback((columnKey: string) => {
16
+ setSortColumn((prevColumn) => {
17
+ if (prevColumn === columnKey) {
18
+ setSortDirection((prevDir) => (prevDir === 'asc' ? 'desc' : 'asc'))
19
+ return columnKey
20
+ }
21
+ setSortDirection('asc')
22
+ return columnKey
23
+ })
24
+ }, [])
25
+
26
+ const resetSort = useCallback(() => {
27
+ setSortColumn(null)
28
+ setSortDirection('asc')
29
+ }, [])
30
+
31
+ return { sortColumn, sortDirection, toggleSort, resetSort }
32
+ }
@@ -0,0 +1,133 @@
1
+ import { useState } from 'react'
2
+ import type { DomainConfig, ColumnConfig, DataGridFilters, SortDirection, ViewMode, DataRow, ViewLayoutConfig } from '../types'
3
+ import { useDataGridSort } from './use-data-grid-sort'
4
+ import { useDataGridFilters } from './use-data-grid-filters'
5
+ import { useDataGridColumns } from './use-data-grid-columns'
6
+ import { useDataGridExpansion } from './use-data-grid-expansion'
7
+ import { useDataGridFavorites } from './use-data-grid-favorites'
8
+ import { useFilteredData } from './use-filtered-data'
9
+ import { useDataGridPagination, type PageSize } from './use-data-grid-pagination'
10
+
11
+ export interface DataGridState {
12
+ // View
13
+ currentView: ViewMode
14
+ setCurrentView: (view: ViewMode) => void
15
+ // Sort
16
+ sortColumn: string | null
17
+ sortDirection: SortDirection
18
+ toggleSort: (columnKey: string) => void
19
+ // Filters
20
+ filters: DataGridFilters
21
+ searchQuery: string
22
+ setFilter: (columnKey: string, value: string[] | null) => void
23
+ setSearchQuery: (query: string) => void
24
+ resetFilters: () => void
25
+ hasActiveFilters: boolean
26
+ activeFilterCount: number
27
+ // Columns
28
+ visibleColumns: Set<string>
29
+ columnOrder: string[]
30
+ columnWidths: Record<string, number>
31
+ toggleColumnVisibility: (columnKey: string) => void
32
+ reorderColumns: (newOrder: string[]) => void
33
+ resizeColumn: (columnKey: string, width: number) => void
34
+ orderedVisibleColumns: Array<[string, ColumnConfig]>
35
+ // Expansion
36
+ expandedRows: Set<string>
37
+ toggleExpand: (rowId: string, expandKey: string) => void
38
+ isExpanded: (rowId: string, expandKey: string) => boolean
39
+ // Favorites
40
+ favorites: Set<string>
41
+ toggleFavorite: (rowId: string) => void
42
+ isFavorite: (rowId: string) => boolean
43
+ // Data
44
+ filteredData: DataRow[]
45
+ pagedData: DataRow[]
46
+ totalCount: number
47
+ filteredCount: number
48
+ // Pagination
49
+ pageSize: PageSize
50
+ setPageSize: (size: PageSize) => void
51
+ currentPage: number
52
+ setCurrentPage: (page: number) => void
53
+ totalPages: number
54
+ // Domain config pass-through
55
+ columnConfig: Record<string, ColumnConfig>
56
+ layout: ViewLayoutConfig
57
+ }
58
+
59
+ /**
60
+ * Orchestrates all data-grid sub-hooks for a given domain configuration.
61
+ *
62
+ * Domain switching:
63
+ * The cleanest reset strategy is to mount the component that calls this hook
64
+ * with `key={domainConfig.key}`. React will unmount and remount the component
65
+ * when the key changes, naturally resetting all hook state. No useEffect or
66
+ * manual reset logic is needed inside this hook for domain switches.
67
+ */
68
+ export function useDataGridState(domainConfig: DomainConfig): DataGridState {
69
+ const [currentView, setCurrentView] = useState<ViewMode>('table')
70
+
71
+ const sort = useDataGridSort()
72
+ const filters = useDataGridFilters(domainConfig.columns)
73
+ const columns = useDataGridColumns(domainConfig.columns)
74
+ const expansion = useDataGridExpansion()
75
+ const favorites = useDataGridFavorites(domainConfig.data)
76
+ const data = useFilteredData(
77
+ domainConfig.data,
78
+ domainConfig.columns,
79
+ filters.filters,
80
+ filters.searchQuery,
81
+ sort.sortColumn,
82
+ sort.sortDirection,
83
+ )
84
+ const pagination = useDataGridPagination(data.filteredData)
85
+
86
+ return {
87
+ // View
88
+ currentView,
89
+ setCurrentView,
90
+ // Sort
91
+ sortColumn: sort.sortColumn,
92
+ sortDirection: sort.sortDirection,
93
+ toggleSort: sort.toggleSort,
94
+ // Filters
95
+ filters: filters.filters,
96
+ searchQuery: filters.searchQuery,
97
+ setFilter: filters.setFilter,
98
+ setSearchQuery: filters.setSearchQuery,
99
+ resetFilters: filters.resetFilters,
100
+ hasActiveFilters: filters.hasActiveFilters,
101
+ activeFilterCount: filters.activeFilterCount,
102
+ // Columns
103
+ visibleColumns: columns.visibleColumns,
104
+ columnOrder: columns.columnOrder,
105
+ columnWidths: columns.columnWidths,
106
+ toggleColumnVisibility: columns.toggleColumnVisibility,
107
+ reorderColumns: columns.reorderColumns,
108
+ resizeColumn: columns.resizeColumn,
109
+ orderedVisibleColumns: columns.orderedVisibleColumns,
110
+ // Expansion
111
+ expandedRows: expansion.expandedRows,
112
+ toggleExpand: expansion.toggleExpand,
113
+ isExpanded: expansion.isExpanded,
114
+ // Favorites
115
+ favorites: favorites.favorites,
116
+ toggleFavorite: favorites.toggleFavorite,
117
+ isFavorite: favorites.isFavorite,
118
+ // Data
119
+ filteredData: data.filteredData,
120
+ pagedData: pagination.pagedData,
121
+ totalCount: data.totalCount,
122
+ filteredCount: data.filteredCount,
123
+ // Pagination
124
+ pageSize: pagination.pageSize,
125
+ setPageSize: pagination.setPageSize,
126
+ currentPage: pagination.currentPage,
127
+ setCurrentPage: pagination.setCurrentPage,
128
+ totalPages: pagination.totalPages,
129
+ // Domain config pass-through
130
+ columnConfig: domainConfig.columns,
131
+ layout: domainConfig.layout,
132
+ }
133
+ }
@@ -0,0 +1,84 @@
1
+ import { useMemo } from 'react'
2
+ import type { DataRow, ColumnConfig, DataGridFilters, SortDirection } from '../types'
3
+
4
+ export interface FilteredDataState {
5
+ filteredData: DataRow[]
6
+ totalCount: number
7
+ filteredCount: number
8
+ }
9
+
10
+ function applySearch(
11
+ data: DataRow[],
12
+ columnConfig: Record<string, ColumnConfig>,
13
+ query: string,
14
+ ): DataRow[] {
15
+ if (query === '') return data
16
+ const lower = query.toLowerCase()
17
+ return data.filter((row) =>
18
+ Object.entries(columnConfig).some(([key, config]) => {
19
+ if (!config.searchable) return false
20
+ const primary = String(row[key] ?? '').toLowerCase()
21
+ if (primary.includes(lower)) return true
22
+ // Also check secondary field for double-text cells
23
+ if (config.secondary) {
24
+ const secondary = String(row[config.secondary] ?? '').toLowerCase()
25
+ return secondary.includes(lower)
26
+ }
27
+ return false
28
+ }),
29
+ )
30
+ }
31
+
32
+ function applyFilters(
33
+ data: DataRow[],
34
+ columnConfig: Record<string, ColumnConfig>,
35
+ filters: DataGridFilters,
36
+ ): DataRow[] {
37
+ return data.filter((row) =>
38
+ Object.entries(filters).every(([key, values]) => {
39
+ if (!values || values.length === 0) return true
40
+ const config = columnConfig[key]
41
+ if (!config) return true
42
+ if (config.filterFn) return values.some((v) => config.filterFn!(row, v))
43
+ return values.includes(String(row[key] ?? ''))
44
+ }),
45
+ )
46
+ }
47
+
48
+ function compareValues(a: unknown, b: unknown): number {
49
+ if (typeof a === 'number' && typeof b === 'number') return a - b
50
+ return String(a ?? '').localeCompare(String(b ?? ''))
51
+ }
52
+
53
+ function applySort(
54
+ data: DataRow[],
55
+ sortColumn: string | null,
56
+ sortDirection: SortDirection,
57
+ ): DataRow[] {
58
+ if (!sortColumn) return data
59
+ return [...data].sort((a, b) => {
60
+ const result = compareValues(a[sortColumn], b[sortColumn])
61
+ return sortDirection === 'asc' ? result : -result
62
+ })
63
+ }
64
+
65
+ export function useFilteredData(
66
+ data: DataRow[],
67
+ columnConfig: Record<string, ColumnConfig>,
68
+ filters: DataGridFilters,
69
+ searchQuery: string,
70
+ sortColumn: string | null,
71
+ sortDirection: SortDirection,
72
+ ): FilteredDataState {
73
+ const totalCount = useMemo(() => data.length, [data])
74
+
75
+ const filteredData = useMemo(() => {
76
+ const searched = applySearch(data, columnConfig, searchQuery)
77
+ const filtered = applyFilters(searched, columnConfig, filters)
78
+ return applySort(filtered, sortColumn, sortDirection)
79
+ }, [data, columnConfig, searchQuery, filters, sortColumn, sortDirection])
80
+
81
+ const filteredCount = useMemo(() => filteredData.length, [filteredData])
82
+
83
+ return { filteredData, totalCount, filteredCount }
84
+ }
@@ -0,0 +1,10 @@
1
+ export { DataGridPage } from './DataGridPage/DataGridPage'
2
+ export type { DataGridPageProps } from './DataGridPage/DataGridPage'
3
+ export { DataGrid } from './DataGrid/DataGrid'
4
+ export type { DataGridProps } from './DataGrid/DataGrid'
5
+ export { DomainSwitcher } from './DomainSwitcher/DomainSwitcher'
6
+ export type { DomainSwitcherProps } from './DomainSwitcher/DomainSwitcher'
7
+ // Types
8
+ export type { DomainConfig, ColumnConfig, DataRow, ViewMode, CellType } from './types'
9
+ // Constants for consumers
10
+ export { DATA_SOURCES, DOMAIN_KEYS } from './constants'
@@ -0,0 +1,103 @@
1
+ export type CellType =
2
+ | 'text'
3
+ | 'double-text'
4
+ | 'link'
5
+ | 'status-badge'
6
+ | 'currency'
7
+ | 'inventory'
8
+ | 'inventory-label'
9
+ | 'progress'
10
+ | 'favorite'
11
+ | 'expand'
12
+
13
+ export type ViewMode = 'table' | 'list' | 'cards'
14
+
15
+ export type SortDirection = 'asc' | 'desc'
16
+
17
+ export type DataRow = Record<string, unknown> & { id: string; favorite?: boolean }
18
+
19
+ export interface ExpandColumnConfig {
20
+ key: string
21
+ label: string
22
+ type?: 'score-bar'
23
+ bold?: boolean
24
+ muted?: boolean
25
+ }
26
+
27
+ export interface ColumnConfig {
28
+ label: string
29
+ type: CellType
30
+ sortable?: boolean
31
+ filterable?: boolean
32
+ primaryFilter?: boolean
33
+ visible?: boolean
34
+ searchable?: boolean
35
+ hideTablet?: boolean
36
+ hideMobile?: boolean
37
+ secondary?: string // for 'double-text'
38
+ statusMap?: Record<string, string> // for 'status-badge' - maps value to CSS class
39
+ currencyField?: string // for 'currency'
40
+ levelFn?: (value: number) => 'high' | 'medium' | 'low' // for 'inventory'
41
+ formatFn?: (value: number) => string // for 'inventory'
42
+ labelMap?: Record<string, string> // for 'inventory-label'
43
+ rowLines?: 1 | 2 | 3
44
+ filterOptions?: string[]
45
+ filterFn?: (row: DataRow, selectedValue: string) => boolean
46
+ expandLabel?: string // for 'expand'
47
+ expandColumns?: ExpandColumnConfig[] // for 'expand'
48
+ expandTitleFn?: (row: DataRow) => string // for 'expand'
49
+ }
50
+
51
+ export interface ListLayoutConfig {
52
+ titleField: string
53
+ metaFields: string[]
54
+ badgeFields: string[]
55
+ valueField: string | null
56
+ expandField: string | null
57
+ }
58
+
59
+ export interface CardRowConfig {
60
+ label: string
61
+ field: string
62
+ rendererOverride?: CellType
63
+ }
64
+
65
+ export interface CardLayoutConfig {
66
+ titleField: string
67
+ subtitleField?: string
68
+ badgeFields: string[]
69
+ rows: CardRowConfig[]
70
+ footerField: string | null
71
+ expandField: string | null
72
+ }
73
+
74
+ export interface ViewLayoutConfig {
75
+ list: ListLayoutConfig
76
+ card: CardLayoutConfig
77
+ }
78
+
79
+ export interface DomainConfig {
80
+ key: string
81
+ label: string
82
+ resultLabel: string
83
+ columns: Record<string, ColumnConfig>
84
+ layout: ViewLayoutConfig
85
+ data: DataRow[]
86
+ }
87
+
88
+ export interface DataGridFilters {
89
+ [columnKey: string]: string[] | null
90
+ }
91
+
92
+ export interface DataGridState {
93
+ currentView: ViewMode
94
+ searchQuery: string
95
+ filters: DataGridFilters
96
+ sortColumn: string | null
97
+ sortDirection: SortDirection
98
+ expandedRows: Set<string> // composite key: `${rowId}::${expandKey}`
99
+ favorites: Set<string>
100
+ visibleColumns: Set<string>
101
+ columnOrder: string[]
102
+ columnWidths: Record<string, number>
103
+ }